繁体   English   中英

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

[英]What did compiler do in this piece of code(Base class pointer to derived class object)?

在这段代码中:

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

编译器做了什么? 以及为什么它如此工作?

这里有UB,因为您要delete类型B数组,该数组具有动态类型D

n3376 5.3.4 / 3:

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

另外,由于要进行多态工作-您应将基类d-tor设为virtual

根本不清楚您要问什么,但是执行此操作时:

B* pb = new D[2];

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

D* pd = new D[2];

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

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

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

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

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

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

但是即使在这种情况下,您也将成为从C继承的C ++不幸功能的受害者。也就是说:数组似乎可以多态工作,但它们却无法工作。

STL容器可防止您遇到此陷阱。 例如:

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

但这不是数组的情况。 您的示例进行了编译,但是随后发生了奇怪的事情,因为您确实有一个D数组,但通过B*使用它。 指针算术失败,因为运行时认为它具有B而不是D的数组,而delete[]再次失败,因为运行时认为它必须删除B的数组而不是D 哦,你可以尝试插入B您的阵列中的D ,当你使用它通过B* ; 这样可以编译,但是在运行时再次失败。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM