我试图弄清楚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?
上运行感谢。
答案 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)。