为什么在复制构造函数中分配联合成员会崩溃?

Why does assigning a union member in the copy constructors crash?

我正在编写一个 class,它通过联合存储几种数据类型之一(在本例中为 QBrushint),并通过使用 [= 记住哪个是活动的14=] 成员.

class X
{
public:
    X(const QBrush &brush)
        : type(0), b(brush) { }
    X(int integer)
        : type(1), i(integer) { }
    X(const X &other)
        : type(other.type)
    {
        if (type == 0)
            b = other.b;
        else i = other.i;
    }

    ~X() { }

private:
    int type;
    union
    {
        QBrush b;
        int i;
    };
};

int main(int argc, char *argv[])
{
    X x = X(QBrush(Qt::red));
    X y = X(x);
    return 0;
}

我很惊讶这个程序崩溃了。目前我没有调试器,所以我只知道它在分配画笔时在 X 的复制构造函数中崩溃。请注意,当我将复制构造函数代码替换为

时它会起作用
X(const X &other)
    : type(other.type), b(other.b), i(other.i)
{ }

这让我更加困惑。

QBrush是Qt提供的一些class。我猜崩溃与此 class 的内部结构有关。但我不知道。有人知道这是怎么回事吗?

这是一个错误:

X(const X &other)
    : type(other.type)
{
    if (type == 0)
        b = other.b;  // b is not constructed yet
    else i = other.i;
}

b 还没有构建,但是你在上面调用了 operator=

使用 new(&b) QBrush(other.b),它使用复制构造函数正确构造 b

我强烈建议您使用 boost::variant<QBrush, int> / std::variant<QBrush, int> (C++17) 而不是尝试自己管理它。在联合中放置非 POD 类型时,您必须手动处理构造和销毁,目前您遗漏了一些案例。至少,您需要一个自定义析构函数,规则 3 规定除了复制构造函数之外,您还应该提供一个赋值运算符。

X(const X &other) : type(1) {
    *this = other;
}
~X() {
    if (type == 0) b.~QBrush();
}
X& operator=(const X &other) {
    if (type == 0) b.~QBrush();
    type = 1;
    if (other.type == 0) {
        new(&b) QBrush(other.b);
        type = 0;
    } else i = other.i;
    return *this;
}

在这里,我改变了 type 的赋值,这样如果 QBrush 构造函数抛出异常,我们就可以避免在展开时调用它的析构函数。