【问题标题】:dynamic_cast on polymorphic class copy segfaults多态类复制段错误上的 dynamic_cast
【发布时间】:2021-10-14 15:46:42
【问题描述】:

以下代码 sn-p 在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();
}

谢谢。

【问题讨论】:

  • 您是否尝试过调试代码?如果您执行reinterpret_cast&lt;T *&gt;(buffer),则取消引用是未定义的行为,因为那里没有T 对象。
  • 你用枪指着你的脸并扣动了扳机,但并不知情。您不能将分配为 uint8_t[32] 的内存转换为对象,您以错误的方式管理内存。 reinterpret_cast 很微妙,应谨慎使用
  • 我不认为它是 dynamic_cast 但可能是对复制构造函数的调用。而且我不知道你为什么要这样做,但这看起来真的很顽皮。
  • @JosephLarson 甚至都不是复制构造函数,那是复制赋值使情况变得更糟。
  • 是的。当我试图弄清楚他想要做什么时,我不禁打了个寒颤。

标签: c++ copy polymorphism dynamic-cast


【解决方案1】:
auto memory{reinterpret_cast<T *>(buffer)};
*memory = *dynamic_cast<T *>(this);

这是不正确的方法,您不能只将某些字节解释为T。对象只能通过调用适当的构造函数来创建,例如初始化虚拟跳转表。

即使在您的上下文中,第二行也是错误的。它调用赋值运算符,正确地假定它的this 是一个活动对象。

正确的方法是使用带有正确对齐存储的放置new,例如std::aligned_storage_t&lt;sizeof(T),alignof(T)&gt;

工作示例:

#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
  • placement new 创建的对象需要显式调用其析构函数。
  • 请不要将指针隐藏在 autotypedefusing 后面,不透明的句柄除外。

【讨论】:

  • 我认为您应该将您的解决方案与 std::aligned_storage_t 转化为示例
  • @IkarusDeveloper 我同意,完成。
猜你喜欢
  • 2019-02-09
  • 1970-01-01
  • 1970-01-01
  • 2015-06-27
  • 1970-01-01
  • 2017-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多