-1

在这段代码中:

#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);

编译器做了什么?为什么它会这样工作?

4

3 回答 3

3

这里有 UB,因为您正在尝试使用具有动态类型delete的 type 数组。BD

n3376 5.3.4/3:

在第二种选择(删除数组)中,如果要删除的对象的动态类型与其静态类型不同,则行为未定义

此外,由于您想以多态方式工作-您应该使用d-torbase class virtual

于 2013-06-17T07:46:37.190 回答
3

根本不清楚你在问什么,但是当你这样做时:

B* pb = new D[2];

您正在动态分配对象数组D,并且B*指向第一个元素。这里

D* pd = new D[2];

您还分配了一个D对象数组,并D*指向第一个元素。

您对这些指针执行的所有指针运算将分别基于 和 的B大小D。这不是您想要的,因为 和 的大小B不必D相同。

要清楚:您没有“指向派生类对象的基类指针”,您有一个指向派生类对象数组的基类指针。

在我看来,您真正想要的是一个指向 的指针数组,然后B您可以指向BD对象,即多态数组。

另请注意,您需要将B' 的析构函数声明为虚拟的,正如已经指出的那样。

于 2013-06-17T08:01:54.017 回答
1

首先,当您尝试通过指针删除对象时,您需要Bto be的析构函数以避免未定义行为 (UB)。virtualDB

但即使在这种情况下,您也会成为从 C 继承的 C++ 的一个不幸特性的受害者。也就是说:数组似乎可以多态地工作,但事实并非如此。

STL 容器可以保护您免受这种陷阱的影响。例如:

std::vector<B> = std::vector<D>(2); // Illegal

但数组并非如此。您的示例编译,但随后发生了奇怪的事情,因为您确实有一个数组D,但通过B*. 指针算术失败是因为运行时认为它有一个数组 inB而不是D,并且delete[]再次失败是因为运行时认为它必须删除一个数组B而不是D。哦,你可以尝试B在你的数组中插入 aD当你通过 a 使用它时B*。这将编译,但在运行时再次惨遭失败。

总之:不要因为你在代码中看到的原因而尝试多态地使用容器。

于 2013-06-17T08:20:22.267 回答