实施出错"Rule of Three"

Implementation of "Rule of Three" gone wrong

本文关键字:Rule of Three 出错 施出错      更新时间:2023-10-16

以下是"三个规则"的错误实现,我试图理解。

调试程序,我发现调试器在清理int *k方面存在问题,可以通过定义int *k = nullptr或简单地将其设置为复制构造函数中合理的东西来解决。

但是,我不明白该程序的结果错误是如何存在的。

我知道,在复制分配构造函数v1int *k之后,不再指向有效的内存地址。

class Vector2 {
public:
    std::string name = "default";
    int* k;
    Vector2(int s, std::string n) : k(new int[s]), name(n) {
    }
    Vector2(const Vector2 &other)  {
        std::cout<< "Copy constructor: " << name << std::endl;
    }
    ~Vector2() {
        std::cout << "Deleting: " << name << std::endl;
        delete[] k;
    }
    void swap(Vector2& other) {
        using std::swap;
        swap(k, other.k);
    }
    Vector2& operator=(Vector2 other) {
        std::cout << "Copy assignment constructor: " << name << std::endl;
        swap(other);
        return *this;
    }
};

int main() {
        Vector2 v1 = Vector2(2, "v1");
        Vector2 v2 = Vector2(4, "v2");
        v1 = v2;
        std::cout << &v1 << " " << &v2 << std::endl;
        std::cout << &v1.k << " " << &v2.k << std::endl;
        return 0;
    }

以下是上述程序的控制台输出:

Copy constructor: default
Copy assignment constructor: v1
Deleting: default
0000001B5611FA28 0000001B5611FA78
0000001B5611FA50 0000001B5611FAA0
Deleting: v2
Deleting: v1
16:18:42: The program has unexpectedly finished.

它实际上非常简单:您的复制构造函数不会制作副本。实际上,它不会初始化任何成员,因此该构造函数创建的任何实例都充满了废话。

对于operator=(Vector2 other)的调用,调用复制构造器来创建other(这是三个规则的点),因此other充满了废话。然后,您将this(又名v1)的有效kotherk交换。

然后,当调用v1的驱动器时,它在糟糕的k上调用delete[] k ->访问违规。

解决方案

使您的复制构造函数制作复制。或至少使其适当初始化k(例如nullptr)。

operator=中构造 other使用复制构造函数,该复制构造函数不会创建尖头的新副本。您的复制构造函数甚至可能不会复制k,因为它是POD类型,因此不一定默认构造或默认复制。

然后,当它被破坏时,它试图将其销毁两次。或取决于诸如堆栈布局之类的随机因素,它可能根本不复制k,然后尝试使用delete无效的指针。

您的问题在Vector2(const Vector2 &other)

您通过按值模糊地隐式地使用operator =中的构造函数;但是您无法将k分配给该构造函数中的任何值。

这会导致交换用无效的k替换有效的k,然后删除无效的k;导致您的崩溃。

可以通过列出事件的确切序列来得出解决方案,例如:更多的打印效果和测试,哪些参数在:

>

从:v1 = v2;

开始
  1. v2调用复制构造函数带有参数其他(其他是),特别是:其int* k并不指向有效的内存。为简单起见,让我们称之为新的vector2 v3。
  2. 复制分配构造函数现在使用V3调用。
  3. 然后我们开始交换。

复制构造函数实际上发生了错误,因为v3在步骤1中未正确初始化。

步骤2和步骤3基本上是"隐藏",将误差从v3传输到v1

现在有趣的问题是,v3如何实际生成?不是默认构造函数!