c ++ ctor和dtor的工作方式不同

时间:2014-04-08 20:50:42

标签: c++ inheritance

我试图弄清楚C ++中类继承的技巧,并且我已经构建了一个示例项目:

#include "stdafx.h"
#include <iostream>
using namespace std;

class A
{
public:
    A()
    {
        cout << "Class A initialized" << endl;
    }

    ~A()
    {
        cout << "Class A destructed" << endl;
    }
};

class B : public A
{
public:
    B()
    {
        cout << "Class B initialized" << endl;
    }

    ~B()
    {
        cout << "Class B destructed" << endl;
    }
};


int _tmain(int argc, _TCHAR* argv[])
{
    cout << "A* a = new A()" << endl;
    A* a = new A();
    cout << "B* b = new B ()" << endl;
    B* b = new B ();
    cout << "A* ab = new B()" << endl;
    A* ab = new B();

    cout << "delete a" << endl;
    delete a;
    cout << "delete b" << endl;
    delete b;
    cout << "delete ab" << endl;
    delete ab;

    int i;
    cin >> i;

    return 0;
}

我得到的输出是:

A* a = new A()
Class A initialized
B* b = new B ()
Class A initialized
Class B initialized
A* ab = new B()
Class A initialized
Class B initialized
delete a
Class A destructed
delete b
Class B destructed
Class A destructed
delete ab
Class A destructed

我可以理解B类作为派生类的行为 - 首先它构造基类然后构造派生类。当它调用析构函数时,它会以相反的方式完成工作。似乎合乎逻辑。

我无法理解的是ab的行为(我将A放入A指针中的B的分配), 为什么构造函数与纯B的行为相同,但析构函数仅在A?

上运行

感谢。

2 个答案:

答案 0 :(得分:2)

编译器调用与指针的静态类型对应的类的成员函数。指针ab的类型是A *,因此编译器调用类A的析构函数。如果您将析构函数声明为虚拟的,例如

class A
{
public:
    //...
    virtual ~A()
    {
        cout << "Class A destructed" << endl;
    }
};

然后编译器将使用vitual函数指针表。在这种情况下,在删除ab的情况下,表将包含引用派生类的析构函数的指针。

对于构造函数,当你使用operator new B()时,表达式中使用的静态类型是B.因此,B的构造函数与A的构造函数一起被调用作为基类的构造函数。

答案 1 :(得分:1)

构造函数和析构函数之间存在根本区别 (或构造函数和任何其他函数,就此而言):何时 构造对象时,必须在源中指定其确切类型 码。对于所有其他函数(包括析构函数),它是 如果满足某些条件,可能只提一个基地。 其中一个条件是函数(或析构函数) 虚基在基类中。

在析构函数的情况下,还有一个额外的约束,因为 析构函数涉及delete,而A* pA;则需要 完整对象的地址,以便正确释放内存。 因此,给定pA->f()f之类的表达式将调用 如果基类不是虚函数,则函数f(),但函数 如果它是虚拟的,则派生类中的delete pA;和派生类 覆盖它。另一方面,pA将调用析构函数 如果基类中的析构函数是虚拟的,则是派生类的,但是 如果std::exception<>指向派生类,则未定义的行为 基础中的析构函数不是虚拟的。没有公正的问题 9总结基类的析构函数;虽然这可能是 在简单的情况下,行为在所有情况下都是不确定的。

出于这个原因,经常建议如果一个班级是 设计用作基类,析构函数应该是 虚拟或受保护。恕我直言,这取决于班级:如果有人 misundertands std::exception<...>* pIter = new std::vector<...>::iterator; // ... delete pIter; 写到:

std::iterator

没有希望,并且不值得定义析构函数的麻烦 对于{{1}},只是为了使它受到保护(并且在预先C ++ 11中,制作 导出的迭代器不可能是POD)。