C++复制构造函数 +指针对象
我正在尝试学习 C++ 中的“三巨头”..我设法为“三巨头”编写了非常简单的程序..但我不确定如何使用对象指针..以下是我的第一次尝试。
当我写这篇文章时我有一个疑问...
问题
- 这是实现默认构造函数的正确方法吗?我不确定我是否需要它。但是我在另一个关于带有指针的复制构造函数的线程中发现,我需要在复制复制构造函数中的地址之前为该指针分配空间。
- 如何在复制构造函数中分配指针变量?我在复制构造函数中编写的方式可能是错误的。
- 我是否需要为复制构造函数和 operatior= 实现相同的代码(除了 return )?
我说我需要删除析构函数中的指针是否正确?
类 TreeNode { 民众: 树节点(); TreeNode(const TreeNode&节点); 树节点&运算符= (const TreeNode&节点); 〜TreeNode(); 私人的: 字符串数据; 树节点*左; TreeNode*右; 朋友类MyAnotherClass; };
实施
TreeNode::TreeNode(){
data = "";
}
TreeNode::TreeNode(const TreeNode& node){
data = node.data;
left = new TreeNode();
right = new TreeNode();
left = node.left;
right = node.right;
}
TreeNode& TreeNode::operator= (const TreeNode& node){
data = node.data;
left = node.left;
right = node.right;
return *this;
}
TreeNode::~TreeNode(){
delete left;
delete right;
}
提前致谢。
I'm trying to learn "big three" in C++.. I managed to do very simple program for "big three".. but I'm not sure how to use the object pointer.. The following is my first attempt.
I have a doubt when I was writing this...
Questions
- Is this the correct way to implement the default constructor? I'm not sure whether I need to have it or not. But what I found in another thread about copy constructor with pointer is that I need to allocate the space for that pointer before copying the address in copy constructor..
- How to assign the pointer variable in the copy constructor? The way that I wrote in Copy Constructor might be wrong.
- Do I need to implement the same code (except return ) for both copy constructor and operatior=?
Am I correct in saying that I need to delete the pointer in destructor?
class TreeNode { public: TreeNode(); TreeNode(const TreeNode& node); TreeNode& operator= (const TreeNode& node); ~TreeNode(); private: string data; TreeNode* left; TreeNode* right; friend class MyAnotherClass; };
Implementation
TreeNode::TreeNode(){
data = "";
}
TreeNode::TreeNode(const TreeNode& node){
data = node.data;
left = new TreeNode();
right = new TreeNode();
left = node.left;
right = node.right;
}
TreeNode& TreeNode::operator= (const TreeNode& node){
data = node.data;
left = node.left;
right = node.right;
return *this;
}
TreeNode::~TreeNode(){
delete left;
delete right;
}
Thanks in advance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
每当设计这样的对象时,首先需要回答一个问题:该对象是否拥有该指针所指向的内存?如果是,那么显然该对象的析构函数需要清理该内存,所以是的,它需要调用delete。这似乎是您对给定代码的意图。
然而,在某些情况下,您可能希望拥有引用其他对象的指针,这些对象的生命周期应该由其他对象管理。在这种情况下,您不想调用删除,因为程序的其他部分有责任这样做。此外,这改变了复制构造函数和赋值运算符的所有后续设计。
我将继续回答其余问题,假设您确实希望每个 TreeNode 对象拥有左侧和右侧对象的所有权。
不可以。您需要将
left
和right
指针初始化为 NULL(如果您愿意,也可以初始化为 0)。这是必要的,因为未初始化的指针可以具有任意值。如果您的代码默认构造了一个 TreeNode,然后销毁它而没有向这些指针分配任何内容,那么无论初始值是什么,都会调用删除。所以在这个设计中,如果这些指针没有指向任何东西,那么你必须保证它们被设置为NULL。left = new TreeNode();
行创建一个新的 TreeNode 对象并将left
设置为指向它。left = node.left;
行重新分配该指针以指向node.left
指向的任何 TreeNode 对象。这有两个问题。问题 1:现在没有任何东西指向新的 TreeNode。它会丢失并成为内存泄漏,因为没有任何东西可以破坏它。
问题 2:现在
left
和node.left
最终都指向同一个 TreeNode。这意味着正在复制构造的对象和从中获取值的对象都将认为它们拥有同一个 TreeNode,并且都将在其析构函数中对其调用 delete。对同一个对象调用两次删除始终是一个错误,并且会导致问题(包括可能的崩溃或内存损坏)。由于每个 TreeNode 都拥有其左节点和右节点,因此最合理的做法可能就是进行复制。所以你会写一些类似的东西:
几乎可以肯定。或者至少,每个代码中的代码应该具有相同的最终结果。如果复制构造和赋值有不同的效果,那将会非常混乱。编辑 - 上面的段落应该是: 每个中的代码应该具有相同的最终结果,因为数据是从另一个对象复制的。这通常会涉及非常相似的代码。但是,赋值运算符可能需要检查是否已将任何内容分配给
left
和right
并清理它们。因此,它可能还需要注意自分配,或者以不会导致自分配期间发生任何不良情况的方式编写。事实上,有多种方法可以使用其中一种来实现另一种,以便操作成员变量的实际代码只写在一个地方。关于SO的其他问题已经讨论过,例如
Whenever designing an object like this, you first need to answer the question: Does the object own the memory pointed to by that pointer? If yes, then obviously the object's destructor needs to clean up that memory, so yes it needs to call delete. And this seems to be your intent with the given code.
However in some cases, you might want to have pointers that refer to other objects whose lifetime is supposed to be managed by something else. In that case you don't want to call delete because it is the duty of some other part of the program to do so. Furthermore, that changes all the subsequent design that goes into the copy constructor and assignment operator.
I'll proceed with answering the rest of the questions under the assumption that you do want each TreeNode object to have ownership of the left and right objects.
No. You need to initialize the
left
andright
pointers to NULL (or 0 if you prefer). This is necessary because unintialized pointers could have any arbitrary value. If your code ever default constructs a TreeNode and then destroys it without ever assigning anything to those pointers, then delete would be called on whatever that initial value is. So in this design, if those pointers aren't pointing at anything, then you must guarantee that they are set to NULL.The line
left = new TreeNode();
creates a new TreeNode object and setsleft
to point to it. The lineleft = node.left;
reassigns that pointer to point to whatever TreeNode objectnode.left
points to. There are two problems with that.Problem 1: Nothing now points to that new TreeNode. It is lost and becomes a memory leak because nothing can ever destroy it.
Problem 2: Now both
left
andnode.left
end up pointing to the same TreeNode. That means the object being copy constructed and the object it's taking values from will both think they own that same TreeNode and will both call delete on it in their destructors. Calling delete twice on the same object is always a bug and will cause problems (including possibly crashes or memory corruption).Since each TreeNode owns its left and right nodes, then probably the most reasonable thing to do is make copies. So you would write something similar to:
Almost certainly. Or at least, the code in each should have the same end result. It would be very confusing if copy construction and assignment had different effects.EDIT - The above paragraph should be: The code in each should have the same end result in that the data is copied from the other object. This will often involve very similar code. However, the assignment operator probably needs to check if anything has already been assigned to
left
andright
and so clean those up. As a consequence, it may also need to watch out for self-assignment or else be written in a way that doesn't cause anything bad to happen during self-assignment.In fact, there are ways to implement one using the other so that the actual code that manipulates the member variables is only written in one place. Other questions on SO have discussed that, such as this one.
更好的是,我认为
你必须在赋值操作中删除指针“左”和“右”,否则会出现内存泄漏
Even better, I think
plus you have to delete the pointers 'left' and 'right' in the assignment operation, or you'll have a memory leak
我就是这样做的:
因为您要管理同一对象中的两个资源,所以正确执行此操作会变得有点复杂(这就是为什么我建议永远不要在一个对象中管理多个资源)。如果您使用复制/交换习惯用法,则复杂性仅限于复制构造函数(要获得正确的强异常保证,这并非易事)。
现在最难的部分是:
This is how I would do it:
Because you are managing two resources in the same object doing this correctly becomes a bit more complex (Which is why I recommend never managing more than one resource in an object). If you use the copy/swap idiom then the complexity is limited to the copy constructor (which is non trivial to get correct for the strong exception guarantee).
The hard part now:
不,对尚未使用
new
分配的内容调用delete
会调用未定义的行为(在大多数情况下,它会导致应用程序崩溃)将指针设置为
NULL
在你的默认构造函数中。我没有看到你的代码的其余部分有这样的问题。您的赋值运算符浅复制节点,而您的复制构造函数深复制它们。
根据您的要求采用合适的方法。 :-)
编辑:
不要在默认构造函数中分配指针,而是使用 初始化列表
No, calling
delete
on something that has not been allocated usingnew
invokes Undefined Behavior (in most cases it results to crash of your application)Set your pointers to
NULL
in your default constructor.I don't see such problems with the rest of your code. Your assignment operator shallow copies the nodes whereas your copy constructor deep copies them.
Follow a suitable approach as per your requirement. :-)
EDIT:
Instead of assigning pointers in your default constructor use an initialization list
我还可以建议使用 boost 库中的 boost::shared_ptr (如果你可以使用它),而不是简单的指针吗?它将解决您可能遇到的许多无效指针、深层复制等问题
May I also suggest boost::shared_ptr from the library boost (if you can used it), instead of simple pointers? It'll solve a lot of problems you might have with invalid pointers, deep copies e.t.c.