C++多重继承和上传的智能指针破坏导致VS 2017中的堆损坏

som*_*one 4 c++ heap inheritance smart-pointers

我使用上面的代码遇到了VS调试器的问题:

class Animal {
public:


};


class Stupid {
public:

};


class Dog : public Stupid, public Animal {
public:


};

int main() {
    std::unique_ptr<Animal> animal = std::unique_ptr<Dog>(new Dog());
    animal.reset();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

执行"animal.reset()"涉及"ntdl.dll"和"wntdll.pdb"后,此代码抛出错误.

以下是MSVC运行时库生成断言失败的表达式,如果我点击"忽略"多次(3)次:

1- _CrtIsValidHeapPointer(block)
2- is_block_type_valid(header->_block_use)
3- HEAP CORRUPTION DETECTED: before Free block (#-50331640) at 0x03737E21. CRT detected that the application wrote to memory before start of heap buffer.
Run Code Online (Sandbox Code Playgroud)

但是如果我改变了Dog的继承顺序,就像这样:

class Dog : public Animal, public Stupid {
public:


};
Run Code Online (Sandbox Code Playgroud)

代码运行正常.

我只在Visual Studio 2017中出现此错误,我尝试使用Ideone,Android Studio,无论继承顺序如何,它都运行良好.

Com*_*sMS 6

这会中断,因为您传递给的指针delete与您从中返回的指针不同new.

向上转换基本上意味着你采用指向派生的指针并假装它是一个指向基础的指针.对于单继承,这只是起作用,因为基本部分始终是存储在派生对象中的第一件事.但是有了多重继承,你有两个基础!

因此,当您向上转换到第二个基数时,您需要更改指针的值以确保它指向的内容实际上是对象的相应基础部分.您可以通过检查调试器中指针的值来验证:

Dog* d = new Dog;
Animal* a = d;
Run Code Online (Sandbox Code Playgroud)

a指针将指向后面一个字节的d指针.

如前所述,可以通过向删除调用中使用的基类类型添加虚拟析构函数来修复此问题(Animal在您的示例中).这将导致编译器生成其他代码,以便在传递指针之前正确地重新调整指针delete.

请注意,gcc实际上在这里实现了空基类优化,因此这个例子可以在那里工作.两个基地将生活在相同的偏移量.当您开始向基类添加非静态数据成员时,它将立即开始打破.