C ++析构函数问题

时间:2009-05-21 17:55:26

标签: c++ destructor

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

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()

8 个答案:

答案 0 :(得分:16)

所发生的事情称为切片。使用类型为Base的对象初始化类型为Derived的对象。由于类型Derived的任何对象都包含类型Base的对象(称为“基类子对象”),因此将有两个Base个对象和一个{{1}整个程序中存在的对象。 Derived对象(及其类型为Derived的基类子对象)仅在初始化时存在,而剩余的Base对象存在直到Base结束。

由于有两个Base对象和一个Derived对象,因此您还将看到另外一个Base destructors运行。

答案 1 :(得分:7)

正在使用复制构造函数。如果您想查看正在发生的事情,请检查复制构造函数:

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

和Derived类似。

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

答案 2 :(得分:4)

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

答案 3 :(得分: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指出的切片。请改为阅读他的答案:)

答案 4 :(得分: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()

答案 5 :(得分:0)

您需要virtual destructor

答案 6 :(得分:0)

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

答案 7 :(得分:0)

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

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

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

4)返回EXIT_SUCCESS;

5)“a”被销毁,因此Base ::〜Base()被称为