在这段代码中:
#include<iostream>
using namespace std;
class B
{
int b;
public:
~B(){ cout <<"B::~B()"<<endl; }//1
};
class D: public B
{
int i,d,e,f;
public:
~D() { cout <<"D::~D()"<<endl; }//2
};
int main(void)
{
cout << "sizeB:" << sizeof(B) << " sizeD:"<< sizeof(D) <<endl;
B *pb = new D[2];
delete [] pb;
return 0;
}
一开始,我不知道delete []如何正常工作。然后我注意到了这一点:
B* pb = new D[2];
&pb[1] - &pb[0] == sizeof(B);
D* pd = new D[2];
&pb[1] - &pb[0] == sizeof(D);
编译器做了什么?为什么它像这样工作?
答案 0 :(得分:3)
此处有UB,因为您正在尝试delete
类型为B
的数组,其动态类型为D
。
n3376 5.3.4 / 3:
在第二种选择中(删除 array)如果要删除的对象的动态类型与其静态类型不同,则行为未定义
此外,由于您希望以多态方式工作,因此您应该创建基类d-tor
的{{1}}。
答案 1 :(得分:3)
你要问的是什么并不清楚,但当你这样做时:
B* pb = new D[2];
您正在动态分配D
个对象的数组,并且您正在向第一个元素发出B*
点。这里
D* pd = new D[2];
您还要分配一组D
个对象,并将D*
指向第一个元素。
您对这些指针执行的所有指针算法将分别基于B
和D
的大小。这不是您想要的,因为B
和D
的大小不必相同。
要明确:您没有“派生类对象的基类指针”,您有一个指向派生类对象数组的基类指针。
在我看来,你真正想要的是一个指针到B
的数组,然后你可以指向B
或D
个对象,即一个多态数组。
另请注意,您需要将B
的析构函数声明为虚拟,正如已经指出的那样。
答案 2 :(得分:1)
首先,当您尝试通过{{删除B
对象时,需要virtual
的析构函数为D
以避免未定义的行为(UB) 1}}指针。
但是即使在这种情况下,你也会成为C ++遗留给C的一个不幸功能的受害者。这就是:数组似乎是多态的,但它们没有。
STL容器可以保护您免受此陷阱的侵害。例如:
B
但阵列并非如此。你的例子编译,但然后发生了奇怪的事情,因为你确实有一个std::vector<B> = std::vector<D>(2); // Illegal
数组,但通过D
使用它。指针算法失败,因为运行时认为它有一个B*
而不是B
的数组,并且D
再次失败,因为运行时认为它必须删除delete[]
的数组B
。哦,当您通过D
使用时,可以尝试在B
数组中插入D
;这会编译,但在运行时再次失败。
总结:不要因为您在代码中看到的原因而尝试以多态方式使用容器。