编译器在这段代码中做了什么(基类指向派生类对象的指针)?

时间:2013-06-17 07:45:35

标签: c++ arrays pointers

在这段代码中:

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

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

3 个答案:

答案 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*指向第一个元素。

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

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

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

另请注意,您需要将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;这会编译,但在运行时再次失败。

总结:不要因为您在代码中看到的原因而尝试以多态方式使用容器。