4

关于下面的示例代码,为什么基类的析构函数被调用了两次?

class Base {
public:
    Base() {
        std::cout << "Base::Base()" << std::endl;
    }

    ~Base() {
        std::cout << "Base::~Base()" << std::endl;
    }
};

class Derived : public Base {
public:
    Derived() {
        std::cout << "Derived::Derived()" << std::endl;
    }

    ~Derived() {
        std::cout << "Derived::~Derived()" << std::endl;
    }
};

int main() {
    Base a = Derived();
    return EXIT_SUCCESS;
}

以下是程序运行时的输出示例:

Base::Base()
Derived::Derived()
Derived::~Derived()
Base::~Base()
Base::~Base()
4

8 回答 8

16

发生的事情称为切片。你用 type 的对象初始化一个 typeBase的对象Derived。由于任何类型Derived的对象也Base包含一个类型的对象(称为“基类子对象”),因此在整个程序中将存在两个Base对象和一个对象。DerivedDerived 对象(及其类型的基类子对象Base)仅在初始化时存在,而其余Base对象存在到 结束main

由于有两个 Base 对象和一个 Derived 对象,您还将看到另外一个 Base 析构函数运行。

于 2009-05-21T18:04:33.790 回答
7

正在使用复制构造函数。如果你想看看发生了什么,也可以检测复制构造函数:

 Base( const Base & ) {
        std::cout << "Base::Base( const Base &)" << std::endl;
    }

Derived 也是如此。

请注意,这与析构函数不是虚拟的无关。

于 2009-05-21T18:04:21.870 回答
4

当您在其中说Derived()时,main()它会创建一个临时对象,然后将其复制到对象 a 中。因此有两个对象,因为析构函数被调用了两次。此外,正如其他人指出的那样,您的基类析构函数应该是虚拟的。

于 2009-05-21T18:00:22.753 回答
2

Derived因为您在使用它进行复制构造之前创建了一个临时类型a。所以这基本上是发生了什么:

Derived d(); // Your temporary of type Derived is created
Base a(d); // The temporary is used to call a's copy constructor 
d.Derived::~Derived(); // The temporary is destroyed, calling both ~Derived and ~Base
a.Base::~Base(); // The nonvirtual destructor on a is called, so ~Base is called, but not ~Derived

因此,除了开始时不必要的复制(编译器可能会优化掉)之外,实际的错误是 ~Base 不是虚拟的。

编辑 糟糕,完全错过了 litb 指出的切片。改为阅读他的答案:)

于 2009-05-21T18:06:17.017 回答
1

添加以下内容将使程序更清晰:

 Base(const Base& base){
        std::cout << "Base::Base(const Base& base)" << std::endl;
 }

编译器会自动为你创建一个拷贝构造函数。通过自己定义(并在打印中添加),您可以看到构造函数和析构函数的数量匹配

Base::Base()
Derived::Derived()
Base::Base(const Base& base)
Derived::~Derived()
Base::~Base()
Base::~Base()
于 2009-05-21T18:14:46.607 回答
0

你需要一个虚拟析构函数

于 2009-05-21T17:59:23.660 回答
0

你有一个堆栈变量和一个临时变量——总共构造了两个对象——所以析构函数被调用两次是合乎逻辑的。

于 2009-05-21T18:07:36.590 回答
0

1) 构建一个 Derived 类型的临时对象(调用 Derived::Derived() 和 Base::Base())

2)临时对象被复制到“a”中

3) 临时对象被销毁(调用 Derived::~Derived() 和 Base::~Base())

4)返回EXIT_SUCCESS;

5) "a" 被销毁,所以 Base::~Base() 被调用

于 2009-05-21T23:06:33.633 回答