类析构函数中无法访问联合体成员

3

我正在实现一个类似于 QVariant 的类,名为 Variant,使用匿名联合:

class Variant
{
public:
    // …

    ~Variant();

    // …

private:
    Type _type;
    union
    {
        int      _int;
        uint64_t _uint64;
        float    _float;
        void*    _ptr;
    };
};

在尝试删除String*时,在Variant::~Variant()中出现段错误,原因是尝试删除Variant::_ptr成员。

Variant::~Variant()
{
    if (_type == Type::String)
        delete reinterpret_cast<String*>(_ptr);
    else if (_type == Type::Date)
        delete reinterpret_cast<Date*>(_ptr);
    // …
}

这是分配的方式:

Variant::Variant(const String& str)
  : _type(Type::String),
    _ptr(new String(str))
{}

有趣的是,在GDB中,对于析构函数的栈帧,我无法访问联合成员,但我可以访问Variant::_type成员。

(gdb) frame
#0  0x0000000000527700 in Variant::~Variant (this=0x7fffec0337a0, __in_chrg=<optimized out>) at Sources/Types/Variant.cpp:85
85                      delete reinterpret_cast<String*>(_ptr);
(gdb) p _ptr
No symbol "_ptr" in current context.
(gdb) p _int
No symbol "_int" in current context.
(gdb) p _float
No symbol "_float" in current context.
(gdb) p _type
$2 = Variant::String

看起来联合已经被删除了,因此,在尝试删除 _ptr 时会发生段错误,因为无法访问它。但是为什么会这样呢?


我怀疑这是“匿名联合”和符号问题。如果你执行 p this->_ptr 会发生什么? - Mats Petersson
看看 Boost.Variant。这是一个干净、高效的实现,具有适当的类型安全 API。通常不建议使用不安全类型的 variant,但如果需要的话,可以考虑使用 Boost.Any,它提供了一个干净、高效、可用的实现。 - Konrad Rudolph
显示该类型的复制构造函数。 - Luc Danton
我可以使用 this->_ptr 访问成员,但仍然会出现段错误。@LucDanton String 没有用户定义的复制构造函数,该类私有继承自 std::string - lesenk
问题中涉及的类型是您的“Variant”。 - Luc Danton
1个回答

3
我的心理调试能力告诉我,你没有实现自己的拷贝构造函数,而默认的拷贝构造函数只是复制了指针,而没有分配新的内存。当原始对象的副本被销毁时,会导致双重删除。
但是,除非这是一项练习,否则只需使用 boost::variant 或 boost::any,因为它们已经是健壮成熟的解决方案。

你说得对,我不习惯包含已分配对象的数据语义对象。现在已经解决了,谢谢。我会看看 boost::variant,但我对它是否适合我的需求持怀疑态度。 - lesenk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接