提问者:小点点

在C继承中,当指向基类的指针对象指向派生类的数组时,不调用派生类析构函数


我有一个带有构造函数和析构函数的动物类。猫有一个私有的大脑*属性。构建时,猫使用new Brain()创建他的大脑;销毁时,猫删除了他的大脑。我不明白为什么猫和大脑的析构函数没有被调用,当我的基本类析构函数是虚拟的?

#include <iostream>

using std::cout ;
using std::endl ;

class Brain {
public:
    Brain (void){cout << "Brain constructor" << endl ;}
    ~Brain (void){cout << "Brain destructor" << endl ;}
} ;

class Animal
{
public:
    Animal (void){cout << "Animal constructor" << endl ;}
    virtual ~Animal (void){cout << "Animal destructor" << endl ;}
} ;

class Cat : public Animal
{
public:
    Cat (void){
                cout << "Cat constructor" << endl ;
        myPointer = new Brain() ;
    }
    ~Cat (void){
        cout << "Cat destructor" << endl ;
        delete myPointer ;
    }
private:
    Brain* myPointer ;
} ;

int main()
{
    const Animal* j = new Cat[1] ;
    delete [] j ;
}

给出输出

Animal constructor
Cat constructor
Brain constructor
Animal destructor

共3个答案

匿名用户

请注意,虽然Cat动物,但Cats数组不是动物s数组。换句话说,数组在C中是不变的,而不是像在其他一些语言中那样是协变的。

所以你正在向上转换这个数组,这后来会混淆编译器。在这种情况下,您必须在正确的原始类型-Cat*上执行数组delete[]

请注意,如果您分配了一个包含2个或更多Cat的数组,将其强制转换为动物*,然后尝试使用第二个或后续的动物,您也会遇到类似的问题。

匿名用户

我回答我自己的评论:https://en.cppreference.com/w/cpp/language/delete指向的表达式类型必须类似于数组对象的元素类型https://en.cppreference.com/w/cpp/language/reinterpret_cast#Type_aliasing

非正式地,两种类型是相似的if,忽略顶级cv资格:

  • 它们是相同的类型;或
  • 它们都是指针,指向的类型相似;或
  • 它们都是指向同一类成员的指针,指向成员的类型相似;或
  • 它们都是大小相同的数组或都是未知界限的数组,数组元素类型相似。(直到C 20)
  • 它们都是大小相同的数组或者至少其中一个是未知界数组,数组元素类型相似。

据我所知,继承不是相似的…

匿名用户

在我的理解中,这是未定义的行为,因为(在7.6.2.9删除,p2,强调我的):

在单对象删除表达式中,删除的操作数的值可能是空指针值,也可能是以前的非数组new-表达式产生的指针值,或者是指向由这种new-表达式创建的对象的基类子对象的指针。如果不是,则行为未定义。在数组删除表达式中,删除的操作数的值可能是空指针值,也可能是以前的数组new-表达式产生的指针值…

这基本上意味着对于delete[],类型必须是new[]中的确切类型(不允许像delete这样的基类子对象)。

因此,出于这样的原因——在我看来,这一次是显而易见的——实现需要知道完整对象大小是多少,以便它可以迭代到下一个数组元素。

计数器参数是因为目前实现无论如何都需要存储数组元素的数量(因此它知道要解构多少)-它也可能存储完整的类型/大小。

对于在底层执行多态捕获匹配的例外情况也是如此(并且也是标准要求的)。