为对象指针实现复制构造函数和运算符的正确方法是啥

Posted

技术标签:

【中文标题】为对象指针实现复制构造函数和运算符的正确方法是啥【英文标题】:What is the proper way to implement a copy constructor and operator for object pointers为对象指针实现复制构造函数和运算符的正确方法是什么 【发布时间】:2020-05-05 12:38:06 【问题描述】:

我正在尝试为 Next 和 Prev 对象指针执行复制构造函数和赋值运算符,但是,当它尝试复制时,我没有得到正确的数据。我不确定是否执行此错误。

节点.cpp

Node::Node(const Node& h) 

    Next = new Node(*h.Next);
    Prev = new Node(*h.Prev);
    data = h.data;


Node::~Node()

    delete Next;
    delete Prev;

Node& Node::operator=(const Node& t) 


    delete Next;
    delete Prev;
    Next = new Node(*t.Next);
    Prev = new Node(*t.Prev);
    data = t.data;
    return *this;


Node.H
Private: 
Node* Next;
Node* Prev;
int data;

【问题讨论】:

为链表的Node 实现复制构造函数和赋值运算符可能会很棘手。实现使用Node 对象的容器的复制构造函数和赋值运算符并不那么棘手。需要考虑的事情。 在我的情况下,我试图复制 Next 和 Prev 指针,当它验证副本时它们应该显示为 0,但事实并非如此。 对于指针,只有指针的所有者才能对其调用 delete。因此,必须只有一个所有者。如果你查看你的节点结构,它有多个所有者(下一个节点和前一个节点都拥有当前节点,因为它们都会在指向当前节点的指针上调用 delete)。 您的问题似乎是 XY 问题。见:What is the XY problem? 为你的链表画一张图,节点为矩形,指针为箭头。现在绘制相同列表的图片加上其中一个节点的副本。您希望新图片看起来像什么?将其发布在您的问题中。 【参考方案1】:
Node::~Node()

    delete Next;
    delete Prev;

当一个节点被销毁时,它会销毁它的继任者。反过来又破坏了它的前任,它破坏了它的继任者,它破坏了它的前任,它破坏了......你能发现问题吗?这种递归是无限的。永无止境。此外,行为未定义,因为您删除了已被删除的指针值。通常,列表操作只有一种方式。您无需返回,因为这就是算法“来自”的地方。

即使我们通过不双向解决问题,递归的另一个问题是它与列表的长度一样深。鉴于大多数系统通常具有有限的调用堆栈大小,这对列表的最大大小施加了隐式限制。一般来说,链表操作应该使用迭代而不是递归。

这两个问题都出现在所有显示的函数中。

我不想在课堂上执行深层复制。

那么您犯了一个错误,因为您的复制构造函数和赋值运算符执行了深层复制 - 或者至少它们看起来像是深层复制的损坏版本。

如果你想要浅拷贝,那么你想要隐式生成的操作做什么。如果您确实想要浅拷贝,那么您还应该希望指针是非拥有的,因此也需要使用隐式析构函数。

【讨论】:

【参考方案2】:

您不应该有Node 的复制构造函数。复制一个节点是没有意义的。

节点是列表的一个元素。副本应该是同一列表中的重复节点吗?但是在列表中的哪个位置?在头部?在末尾?一开始?没有意义。

制作副本应该是不同列表中的重复节点吗?但是在什么列表中?再说一遍,这毫无意义。

为什么你认为你的Node 类应该有一个复制构造函数?你觉得它应该怎么做?

如果您对此有答案,请创建一个复制构造函数。如果你不这样做,不要

同样的逻辑也适用于赋值运算符。它应该怎么做?没有明显的答案。所以除非你对它应该做什么有一个准确的想法,否则不要写它。

【讨论】:

我给你举个例子。我将节点 B 复制到节点 C。节点 c.GetNext 需要为 0,因为这就是节点 B 的含义,但在我的情况下它不是。 我或多或少只是想复制一个指针对象。我不想在课堂上执行深层复制。 @Jay 我还是不明白你在做什么。假设一个节点是列表中间的第四个节点。您制作该节点的副本。副本应该是哪个列表的第四个节点?那也应该是同一个列表的第四个节点,所以列表现在有两个第四个节点吗?您没有制作任何指针或引用的副本,您的代码是 Node 的复制构造函数,用于制作 Node 的副本。 另外,你的析构函数没有意义。为什么要同时破坏前一个节点和下一个节点?这意味着前一个节点也会破坏这个节点(因为这个节点是它的下一个节点),下一个节点也会破坏这个节点(因为这个节点是它的前一个节点),因此破坏一个节点会破坏它 3 次。这也没有任何意义。绝对没有理由破坏一个节点也应该破坏其他节点。 (如果破坏这个节点的代码也想破坏其他节点,那是它的事。) (当你为单个节点实例编写析构函数时,它应该破坏节点的单个实例。它不应该尝试破坏其他任何东西,因为这是其他代码的工作。)

以上是关于为对象指针实现复制构造函数和运算符的正确方法是啥的主要内容,如果未能解决你的问题,请参考以下文章

在c++中虚函数和多态性是啥意思

如何为具有自引用指针的类实现复制构造函数/赋值运算符?

包含指向派生模板类的基类指针的类的赋值运算符和复制构造函数

防止 C++ 中的意外对象复制

c++中拷贝构造函数和赋值运算符重载本质上一样么

这样做的正确方法是啥?调用指针到指针到指针中的函数?