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

What is the proper way to implement a copy constructor and operator for object pointers

提问人:Jay 提问时间:1/19/2020 更新时间:1/19/2020 访问量:104

问:

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

Node.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;
C++ 链表 析构函数 复制构造函数赋 值运算符

评论

0赞 R Sahu 1/19/2020
为链表实现复制构造函数和赋值运算符可能很棘手。实现使用该对象的容器的复制构造函数和赋值运算符不那么棘手。要考虑的事情。NodeNode
0赞 Jay 1/19/2020
就我而言,我试图复制下一个和上一个指针,当它验证副本时,它们应该显示为 0,但事实并非如此。
2赞 Martin York 1/19/2020
对于指针,只有指针的所有者才能对其调用 delete。因此,必须只有一个所有者。如果你看一下你的节点结构,它有多个所有者(下一个和上一个节点都拥有当前节点,因为它们都会在指向当前节点的指针上调用 delete)。
0赞 David C. Rankin 1/19/2020
您的问题似乎是 XY 问题。请参阅:什么是 XY 问题?
0赞 n. m. could be an AI 1/19/2020
绘制链表的图片,节点为矩形,指针为箭头。现在绘制同一列表的图片以及其中一个节点的副本。你希望新图片是什么样子的?将其发布在您的问题中。

答:

1赞 David Schwartz 1/19/2020 #1

不应具有 的复制构造函数。创建节点的副本是没有意义的。Node

节点是列表的元素。副本是否应该是同一列表中的重复节点?但是在列表中的哪个位置?在头上?最后?一开始?这毫无意义。

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

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

如果你有答案,那么创建一个复制构造函数。如果你不这样做,就不要

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

评论

0赞 Jay 1/19/2020
我给你举个例子。我将节点 B 复制到节点 C 中,节点 c.GetNext 需要为 0,因为这就是节点 B 的情况,但在我的情况下,它不是。
0赞 Jay 1/19/2020
我或多或少只是在尝试复制指针对象。我没有尝试在课堂上执行深度复制。
1赞 David Schwartz 1/19/2020
@Jay 我仍然不明白你想做什么。假设一个节点是列表中间的第四个节点。创建该节点的副本。副本应该是哪个列表的第四个节点?这是否也应该是同一列表的第四个节点,以便该列表现在有两个第四个节点?您不是在复制任何指针或引用,您的代码是用于复制 NodeNode 的复制构造函数。
1赞 David Schwartz 1/19/2020
此外,你的析构函数没有意义。为什么要同时销毁上一个节点和下一个节点?这意味着前一个节点也将销毁这个节点(因为这个节点是它的下一个节点),下一个节点也将销毁这个节点(因为这个节点是它的上一个节点),因此销毁一个节点将销毁它三次。这也说不通。绝对没有理由摧毁一个节点也应该摧毁其他节点。(如果破坏这个节点的代码也想破坏其他节点,那就是它的事。
0赞 David Schwartz 1/19/2020
(当您为 Node 的单个实例编写析构函数时,它应该销毁 Node 的单个实例。它不应该试图破坏其他任何东西,因为这是其他代码的工作。
1赞 eerorika 1/19/2020 #2
Node::~Node()
{
    delete Next;
    delete Prev;
}

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

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

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

我没有尝试在课堂上执行深度复制。

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

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