这个C++测验答案背后的解释是什么

What is the explanation behind the answer to this C++ quiz?

本文关键字:解释 是什么 背后 答案 C++ 这个      更新时间:2023-10-16

>我在C++测验中遇到了这个问题(初学者C++):我的答案不正确,我想了解正确答案背后的解释 - "未定义的行为"

问题:函数 foo() 返回后,以下代码会发生什么?

class base
{
    public:
        base() { }
        ~base() { }
};
class derived : public base
{
    private:
        int *p_pi_values;
    public:
        derived() : p_pi_values(new int[100]) {  }
        ~derived() { delete [] p_pi_values; }
};
void foo(void)
{
    derived *p_derived = new derived();
    base *p_base = p_derived;
    // Do some other stuff here.
    delete p_base;
}

我给出了这个答案,结果是错误的==>整数数组将无法正确删除。

正确答案 ==> 行为未定义。

类的析构函数不是virtual 。这只是语言的规则,如果通过指向基子对象的指针删除对象,则相应的基类必须具有虚拟析构函数,否则就是未定义的行为。

(实际上,如果基类没有虚拟析构函数,则编译器可能不会发出必要的代码来执行派生对象的所有必要清理。它只会假设你的对象与指针属于同一类型,而懒得进一步查看,因为事实上,对派生最多的对象的多态查找是以你不想不必要地强加的代价的。

§5.3.5/3:

在第一种替代方法(删除对象)中,如果操作数的静态类型与其动态类型不同,则静态类型应为操作数的动态类型的基类,静态类型应具有虚拟析构函数或行为未定义

你应该在基类中使析构函数成为虚拟的。现在代码的问题在于delete p_base将导致调用基类的析构函数。不会调用派生类中的 one,也不会释放为整数数组分配的内存。

发生这种情况是因为如果一个方法在基类中不是虚拟的,编译器只是查看指针类型并调用位于该类型中的方法(在本例中 - 它是一个基类),即在编译期间根据指针的类型而不是指针引用的对象的实际类型来决定调用什么方法。

出于好奇,我检查了C++规格。问题的答案是第 5.3.5 节中的第 3 项:

在第一个备选方案(删除对象)中,如果静态类型 要删除的对象不同于其动态类型,即静态 类型应是要成为的对象的动态类型的基类 已删除,静态类型应具有虚拟析构函数或 行为未定义。

就个人而言,我会以与您相同的方式回答。如果忽略编译器的警告,则最可能的结果是不会调用派生类的析构函数。

我想编译器可以优化此代码,因此永远不会发生p_derived分配给p_base。

更具体地说,编译器可以将代码优化为一行。

删除新派生();

因此,可以认为,未定义的行为可能会改变编译器真正优化代码的方式。