2

下面是我试图理解的“三法则”的错误实现。

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

但是,我不明白程序的结果错误(访问冲突)是如何产生的。

我知道,在复制赋值构造函数不再指向有效的内存地址v1之后。int *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.
4

4 回答 4

3

它实际上非常简单:您的复制构造函数不会复制。实际上,它并没有初始化任何成员,所以这个构造函数创建的任何实例都充满了废话。

对于operator=(Vector2 other)复制构造函数的调用是调用create other(这就是规则三的要点),所以other是废话。然后你将有效kthis(aka v1) 与糟糕kother.

然后,当调用 的析构函数时v1,它会调用delete[] k一个糟糕的k--> 访问冲突。

解决方案

使您的复制构造函数进行复制。或者至少,使其正确初始化k(例如 to nullptr)。

于 2019-03-13T16:24:54.250 回答
0

构造otherinoperator=使用复制构造函数,它不会创建指向值的新副本。您的复制构造函数甚至可能不会复制k,因为它是 POD 类型,因此不一定是默认构造或默认复制。

然后当它被破坏时,它会尝试破坏它两次。或者根据堆栈布局等随机因素,它可能根本不复制k,然后尝试delete指向无效指针。

于 2019-03-13T15:56:38.453 回答
0

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

operator =通过值传递隐式地使用此构造函数;但是您未能将 k 分配给该构造函数中的任何值。

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

于 2019-03-13T16:22:35.207 回答
0

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

开始于:v1 = v2;

  1. v2使用参数 other(无论 other 是什么)调用复制构造函数int* k,特别是:它不指向有效内存。为简单起见,我们称这个新的 Vector2 v3。
  2. 现在使用 v3 调用复制赋值构造函数。
  3. 然后我们开始交换。

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

第 2 步和第 3 步基本上是“隐藏”,将错误v3v1.

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

于 2019-03-13T17:20:24.547 回答