为什么多态不适用于C++中的数组?

xml*_*lmx 16 c++ polymorphism virtual standards destructor

#include <iostream>

using namespace std;

struct Base
{
    virtual ~Base()
    {
        cout << "~Base(): " << b << endl;
    }

    int b = 1;
};

struct Derived : Base
{
    ~Derived() override
    {
        cout << "~Derived(): " << d << endl;
    }

    int d = 2;
};

int main()
{
    Base* p = new Derived[4];
    delete[] p;
}
Run Code Online (Sandbox Code Playgroud)

输出如下:(带有Clang 3.8的Visual Studio 2015)

~Base(): 1
~Base(): 2
~Base(): -2071674928
~Base(): 1
Run Code Online (Sandbox Code Playgroud)

为什么多态不适用于C++中的数组?

R S*_*ahu 20

鉴于,

Base* p = Derived[4];
Run Code Online (Sandbox Code Playgroud)

C++ 11标准制作

delete [] p;
Run Code Online (Sandbox Code Playgroud)

是未定义的行为.

5.3.5删除

...

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

从内存布局的角度来看,为什么delete [] p;会导致未定义的行为也是有道理的.

如果sizeof(Derived)N,new Derived[4]分配内存将是这样的:

+--------+--------+--------+--------+
|   N    |   N    |   N    |   N    |
+--------+--------+--------+--------+
Run Code Online (Sandbox Code Playgroud)

一般来说,sizeof(Base)<= sizeof(Derived).在您的情况下,sizeof(Base)< sizeof(Derived)since Derived有一个额外的成员变量.

当你使用:

Base* p = new Derived[4];
Run Code Online (Sandbox Code Playgroud)

你有:

p
|
V
+--------+--------+--------+--------+
|   N    |   N    |   N    |   N    |
+--------+--------+--------+--------+
Run Code Online (Sandbox Code Playgroud)

p+1指向第一个对象中间的某个位置sizeof(Base) < sizeof(Derived).

       p+1
       |
       V
+--------+--------+--------+--------+
|   N    |   N    |   N    |   N    |
+--------+--------+--------+--------+
Run Code Online (Sandbox Code Playgroud)

当调用析构函数时p+1,指针不指向对象的开头.因此,该程序表现出不确定行为的症状.


相关问题

由于尺寸的差异BaseDerived,你不能遍历使用动态分配的数组中的元素p.

for ( int i = 0; i < 4; ++i )
{
   // Do something with p[i]
   // will not work since p+i does not necessary point to an object
   // boundary.
}
Run Code Online (Sandbox Code Playgroud)


das*_*ght 14

您得到未定义的行为,因为操作员delete[]不知道数组中存储了什么类型的对象,因此它信任静态类型来决定单个对象的偏移量.标准说明如下:

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

数组的静态类型需要匹配用于分配的元素类型:

Derived* p = new Derived[4]; // Obviously, this works
Run Code Online (Sandbox Code Playgroud)

本问答详细介绍了该标准有此要求的原因.

至于修复此行为,您需要创建一个指针数组,常规或智能(最好是智能以简化内存管理).