使用复制构造函数创建对象(三的简单规则 Class)会产生 运行 次错误

Creating object with Copy Constructor (Simple Rule of Three Class) yields run-time error

我有下面的简单程序:

#include <iostream>

class Counter
{
private:
    size_t m_count;
public:
    Counter() :
        m_count(0)
    {
        std::cout << "defctor" << '\n';
    }
    Counter(size_t count) :
        m_count(count)
    {
        std::cout << "ctor" << '\n';
    }
    ~Counter() {
        std::cout << "dtor" << '\n';
    }
    Counter(const Counter& rhs) :
        m_count{0}
    {
        Counter temp{rhs.m_count};
        std::cout << "cctor" << '\n';
        std::swap(*this, temp);
    }
    Counter& operator=(const Counter& rhs)
    {
        if (this == &rhs) {
            return *this;
        }
        Counter temp{rhs.m_count};
        std::swap(*this, temp);
        std::cout << "caop" << '\n';
        return *this;
    }

    constexpr int getCount() const noexcept {
        return this-> m_count;
    }
};


int main() {
    Counter x{1};
    Counter y;
    Counter z{x}; // this fails
}

我试图构建一个简单的三规则 class。我在这一行 Counter z{x}; 上得到 UB,它应该调用复制构造函数。输出:

ctor
defctor
ctor
cctor
ctor
cctor
ctor
cctor
ctor
cctor
ctor
cctor
ctor
cctor
ctor
cctor
ctor
cctor
ctor
cctor
ctor
cctor
ctor
cctor
ctor
cctor
ctor

然后重复 ctor\ncctor ...

我已经有一段时间没有使用 C++ 了。我就是找不到错误!

std::swap 仅在您定义了一个移动赋值运算符时才使用它(一旦您添加了几乎任何其他特殊成员函数,编译器将不会定义一个)。你没有,所以它退回到复制作业。

但是您的复制赋值运算符是根据 std::swap 定义的。这当然是一个无穷无尽的递归:要交换你需要赋值,而赋值你需要交换。最终你会得到一个堆栈溢出。

您可以直接在复制构造函数中初始化 m_count(并直接在复制赋值运算符中修改它)。看起来你正在做一半的复制和交换习语,但我不确定你真正想要的是什么。

是的,在现代 C++ 中,您还应该在适当的地方实现移动构造函数。如果操作正确,这将修复 std::swap 递归。我建议您查看一些示例,了解如何正确实现这些特殊成员函数。

在我看来,您应该像这样更改您的复制构造函数:

Counter(const Counter& rhs)
{
    m_count=rhs.m_count;
}

足够了。结果是:

ctor
defctor
dtor
dtor
dtor
Press any key to continue ...