c++ - 派生类的新位置
问题描述
C++ 大师。需要你的帮助来处理这个小挠头:
#include <iostream>
struct B{
virtual ~B() = default;
virtual void talk() { std::cout << "Be-e-e\n"; }
};
struct D:B{
void talk() override { std::cout << "Duh\n"; }
~D() { std::cout << "~D()\n"; }
};
int main(){
B b{}; // vptr points to B
new (&b) D; // vptr now points to D
b.talk(); // "Be-e-e" (why? shouldn't the vptr be used?)
b = D{}; // "~D()" (why? shouldn't the copying be elided?)
b.talk(); // "Be-e-e"
B*b1{new D};
b1->talk(); // "Duh"
delete b1; // "~D()"
return 0;
}
代码非常简单:在堆栈上有一个基础对象,将派生对象放入其中(是的,eeew,但请耐心等待)并调用虚拟方法,期望打印派生的输出。
实际输出
上面的代码产生以下输出:
蜜蜂 〜D() 蜜蜂 呵呵 〜D()
在我尝试过的 MSVC、gcc、clang 和一些在线编译器上普遍观察到这种行为(这非常强烈地表明是我错了)。
第1部分
Placement-new 将派生类型对象重新更新到基本类型内存中。这会更新 vptr 以指向派生类型的 vtable(直接在调试器中观察到)。
主要问题:这是预期的行为吗?(我想说“是”,所以如果不是 - 请向我解释)
我想相信执行新放置(假设派生类型对象有足够的内存)应该就地初始化派生类型的全新对象。
如果我的理解是正确的,那么第一个b.talk()
应该输出"Duh"
,因为现在的对象是派生类型的。为什么还在打印"Be-e-e"
?
将派生类型对象分配给基类型对象(除了导致对象拼接之外)不会复制 vptr,因此"Be-e-e"
当我们到达代码。
第2部分
为什么任务中有~D()
电话b = D{};
?难道它不是一个临时的,应该被复制省略而不需要对该临时的析构函数调用吗?
第 3 部分
最后一个使用指针的代码块“按预期”工作,只是在这里进行完整性检查
解决方案
看代码:
B b{}; // vptr points to B
new (&b) D; // vptr now points to D
这是一个潜在的问题,原因有两个。首先,您没有调用基础对象的析构函数B
。其次,大小B
可能太小而无法容纳D
类型对象。
b.talk(); // "Be-e-e" (why? shouldn't the vptr be used?)
虚拟调用仅在通过指针或引用调用时起作用。像这样的直接函数调用从不使用虚拟调度。
b = D{}; // "~D()" (why? shouldn't the copying be elided?)
因为b
被声明为类型B
,你不能省略不同类型之间的副本,比如D
和B
。
推荐阅读
- r - 如何在R中找到数据框的概率?
- c++ - 基于预处理器指令定义自定义 MPI 类型
- javascript - 如何在 List.js 中获取 List 实例
- reactjs - react component load 5 times 'undefined' then it finds the inner html div
- javascript - JavaScript script working in HTML code tester but not on website
- flutter - Class
has no instance getter 'documents'.Reciever : Instance of 'QuerySnapshot' - javascript - Add craco webpack plugin by condition for create react app
- reactjs - sharing data between disconnected components
- tensorflow - Predict should return zero if no train data available in one numeric feature column
- javascript - 如果至少选中了一个复选框,则返回 true,否则返回 false