下面的代码片段导致dynamic_cast
。有人能给我解释一下为什么会这样吗?
#include <cassert>
#include <cstdint>
uint8_t buffer[32];
class Top {
public:
virtual ~Top() = default;
};
template <typename T>
class Middle : public Top {
public:
void copy() {
// Create copy of class instance in buffer
auto memory{reinterpret_cast<T *>(buffer)};
*memory = *dynamic_cast<T *>(this);
// Upcast, works
Top * topPtr{memory};
assert(topPtr != nullptr);
// Downcast, causes segmentation fault, why?
auto bottomPtr{dynamic_cast<T *>(topPtr)};
}
};
class Bottom : public Middle<Bottom> {
};
int main() {
Bottom b;
b.copy();
}
谢谢。
auto memory{reinterpret_cast<T *>(buffer)};
*memory = *dynamic_cast<T *>(this);
这是不正确的方法,您不能仅仅将一些字节解释为T
。对象只能通过调用适当的构造函数来创建,例如初始化虚拟跳转表。
第二行即使在你的上下文中也是错误的。它调用赋值操作符,该操作符理所当然地假定它的this
是一个活动对象。
正确的方法是使用放置new
与正确对齐的存储,例如std::aligned_storage_t<sizeof(T),alignof(T)>
。
工作的例子:
#include <cstdint>
#include <cassert>
#include <type_traits>
#include <new>
class Top {
public:
virtual ~Top() = default;
};
template <typename T>
class Middle : public Top {
public:
void copy() {
std::aligned_storage_t<sizeof(T),alignof(T)> buffer;
// Create a new T object. Assume default construction.
auto* memory = new(&buffer)T();
// Copy the object using operator=(const T&)
*memory = *dynamic_cast<T *>(this);
// Upcast, works
Top * topPtr{memory};
assert(topPtr != nullptr);
// Downcast also works.
auto* bottomPtr{dynamic_cast<T *>(topPtr)};
// Using placement new requires explicit call to destructor.
memory->~T();
}
};
class Bottom : public Middle<Bottom> {
};
int main() {
Bottom b;
b.copy();
}
- 生命周期从一个构造函数调用开始,你不能绕过它,如果它需要参数,你必须传递它们。
operator=
是复制对象的正确方法,除非你真的知道你在做什么,否则不要使用std::memcpy
。 通过放置new创建的对象需要显式调用其析构函数。- 请不要在
auto
,typedef
或using
后面隐藏指针,不透明句柄除外。