The following code snippet causes a segmentation fault during the dynamic_cast
. Can anybody explain to me why this happens?
#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();
}
Thanks.
auto memory{reinterpret_cast<T *>(buffer)};
*memory = *dynamic_cast<T *>(this);
This is incorrect approach, you cannot just interpret some bytes as T
. Objects are created only by calling the appropriate constructors which for example initialize the virtual jump table.
The second line is also wrong even in your context. It calls the assignment operator, that rightfully assumes its this
is an alive object.
The correct way is using placement new
with correctly aligned storage, for example std::aligned_storage_t<sizeof(T),alignof(T)>
.
Working example:
#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=
is the correct approach to copy objects, do not use std::memcpy
unless you really know what you are doing.auto
, typedef
,or using
with exception of opaque handles.