其他分享
首页 > 其他分享> > c-当用非虚拟析构函数“删除”基类时,Clang和GCC会做什么?

c-当用非虚拟析构函数“删除”基类时,Clang和GCC会做什么?

作者:互联网

已经有a question询问删除缺少虚拟析构函数的基类的指针的“现实世界”行为,但问题仅限于非常有限的情况(派生类没有具有非平凡析构函数的成员) ,并且可接受的答案只是说,如果不检查每个编译器的行为,就无法知道.

….但这实际上不是很有帮助;知道每个编译器的行为可能都不同,这并不能告诉我们任何特定编译器的行为.那么,在这种情况下Clang和G会做什么?我假设他们只是调用基类的析构函数,然后释放内存(对于整个派生类).是这样吗

或者,如果无法针对所有版本的GCC和Clang确定这一点,那么GCC 4.9和5.1以及Clang 3.5到3.7怎么样?

解决方法:

首先,标准免责声明:这是未定义的行为,因此即使使用一个特定的编译器,更改编译器标志,一周中的一天或查看计算机的方式也可能会更改行为.

以下所有条件都假设您在析构函数中发生了至少某种程度的微不足道的破坏(例如,对象删除了一些内存,或者包含其他对象本身也删除了一些内存).

在简单情况下(单继承),您通常会获得大致等同于静态绑定的内容-也就是说,如果您通过指向基础对象的指针销毁了派生对象,则仅会调用基础构造函数,因此该对象不会被正确销毁.

如果您使用多重继承,并且通过“第一个”基类破坏了派生类的对象,则该对象通常与您使用单次继承大致相同-基类析构函数将被调用,但是派生类析构函数不会.

如果您具有多重继承并通过指向第二个(或后续)基类的指针销毁派生对象,则程序通常会崩溃.通过多重继承,您可以在派生对象中的多个偏移处具有多个基类对象.

enter image description here

在典型情况下,第一个基类将在派生对象的开头,因此使用派生的地址作为指向第一个基类对象的指针的工作原理与单继承情况下的工作原理相同-我们得到了等效的结果静态绑定/静态调度.

如果我们对其他任何基类进行尝试,则指向派生类的指针不会指向该基类的对象.在将指针完全用作指向该类型对象的指针之前,需要对其进行调整以指向第二(或后续)基类.

使用非虚拟析构函数时,通常会发生的情况是,代码将基本上使用该第一个基类对象的地址,对其进行大致相当于reinterpret_cast的尝试,然后尝试像使用对象一样使用该内存指针指定的基类的名称(例如,base2).例如,假设base2在偏移量14处有一个指针,而base2的析构函数试图删除它指向的内存块.使用非虚拟的析构函数,它可能会收到指向base1主题的指针-但它仍然会从那里查看偏移量14,并尝试将其视为指针,然后将其传递给delete.可能是base1在该偏移量处包含一个指针,并且实际上是指向一些动态分配的内存,在这种情况下,这实际上似乎可以成功.再说一遍,也可能是完全不同的情况,程序死于一条错误消息,例如(关于)试图释放无效指针的消息.

base1的大小也可能小于14个字节,因此最终实际上是在base2中操纵(例如)偏移量4.

底线:在这种情况下,事情真的很匆忙.您可以希望的最好的办法是,该程序快速且响亮地死掉.

只是为了踢球,快速演示代码:

#include <iostream>
#include <string>
#include <vector>

class base{ 
    char *data;
    std::string s;
    std::vector<int> v;
public:
    base() { data = new char;  v.push_back(1); s.push_back('a'); }
    ~base() { std::cout << "~base\n"; delete data; }
};

class base2 {
    char *data2;
public:
    base2() : data2(new char) {}
    ~base2() { std::cout << "~base2\n"; delete data2; }
};

class derived : public base, public base2 { 
    char *more_data;

public:
    derived() : more_data(new char) {}
    ~derived() { std::cout << "~derived\n"; delete more_data; }
};

int main() {
    base2 *b = new derived;
    delete b;
}

g / Linux:分段错误
clang / Linux:分段错误
VC / Windows:弹出窗口:“ foo.exe已停止工作”“出现问题,导致程序无法正常工作.请关闭程序.”

如果将指针更改为base而不是base2,则从所有编译器获取〜base(如果仅从一个基类派生,并使用指向该基类的指针,则得到的结果相同:仅该基类的析构函数运行).

标签:virtual-destructor,c,undefined-behavior,clang,gcc
来源: https://codeday.me/bug/20191011/1889622.html