C++ Copy Constructor + Pointer Object
我正在努力学习C++中的"大三"。我为"三巨头"做了非常简单的程序。但我不知道如何使用对象指针。以下是我的第一次尝试。
我在写这篇文章的时候有点怀疑…
问题
我说我需要删除析构函数中的指针是正确的吗?
1 2 3 4 5 6 7 8 9 10 11 12 13 | class TreeNode { public: TreeNode(); TreeNode(const TreeNode& node); TreeNode& operator= (const TreeNode& node); ~TreeNode(); private: string data; TreeNode* left; TreeNode* right; friend class MyAnotherClass; }; |
实施
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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; } |
事先谢谢。
Am I correct in saying that I need to delete the pointer in destructor?
每当设计这样的对象时,首先需要回答一个问题:对象是否拥有指针指向的内存?如果是,那么显然对象的析构函数需要清除内存,所以是的,它需要调用delete。这似乎是您对给定代码的意图。
但是,在某些情况下,您可能希望有指针指向其他对象,这些对象的生存期应该由其他对象管理。在这种情况下,您不想调用delete,因为这样做是程序其他部分的职责。此外,这将更改进入复制构造函数和赋值运算符的所有后续设计。
我将继续回答剩下的问题,假设您确实希望每个Treenode对象都拥有左对象和右对象的所有权。
Is this the correct way to implement the default constructor?
不需要。您需要将
How to assign the pointer variable in the copy constructor? The way that I wrote in Copy Constructor might be wrong.
行
问题1:现在没有什么能指向新的树烯酮。它会丢失并成为内存泄漏,因为没有任何东西可以破坏它。
问题2:现在,
因为每个树节点都有自己的左节点和右节点,所以最合理的做法可能是复制。所以你可以写一些类似的东西:
1 2 3 4 5 6 7 8 9 10 | TreeNode::TreeNode(const TreeNode& node) : left(NULL), right(NULL) { data = node.data; if(node.left) left = new TreeNode(*node.left); if(node.right) right = new TreeNode(*node.right); } |
Do I need to implement the same code (except return ) for both copy constructor and operatior=?
几乎可以肯定。或者至少,每个代码都应该有相同的最终结果。如果复制构造和分配有不同的效果,这将非常令人困惑。
编辑-上面的段落应该是:每个代码的最终结果应该与从另一个对象复制数据的结果相同。这通常涉及非常相似的代码。但是,分配操作员可能需要检查是否有任何东西已经分配给了
事实上,有一些方法可以使用另一种方法实现其中一个,这样操作成员变量的实际代码就只写在一个地方。其他问题已经讨论过了,比如这个问题。
我觉得更好
1 2 3 4 | TreeNode::TreeNode():left(NULL), right(NULL) { // data is already set to"" if it is std::string } |
另外,您必须删除赋值操作中的指针"左"和"右",否则会出现内存泄漏。
我就是这样做的:因为您在同一个对象中管理两个资源,所以正确地执行此操作会变得更加复杂(这就是为什么我建议不要在一个对象中管理多个资源的原因)。如果您使用复制/交换习惯用法,那么复杂性仅限于复制构造函数(这对于获得强异常保证的正确性是非常重要的)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | TreeNode::TreeNode() :left(NULL) ,right(NULL) {} /* * Use the copy and swap idium * Note: The parameter is by value to auto generate the copy. * The copy uses the copy constructor above where the complex code is. * Then the swap means that we release this tree correctly. */ TreeNode& TreeNode::operator= (const TreeNode node) { std::swap(data, node.data); std::swap(left, node.left); std::swap(right, node.right); return *this; } TreeNode::~TreeNode() { delete left; delete right; } |
现在最难的部分是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | /* * The copy constructor is a bit harder than normal. * This is because you want to provide the `Strong Exception Guarantee` * If something goes wrong you definitely don't want the object to be * in some indeterminate state. * * Simplified this a bit. Do the work that can generate an exception first. * Once all this has been completed we can do the work that will not throw. */ TreeNode::TreeNode(const TreeNode& node) { // Do throwable work. std::auto_ptr<TreeNode> nL(node.left == null ? null : new TreeNode(*node.left)); std::auto_ptr<TreeNode> nR(node.right == null ? null : new TreeNode(*node.right)); // All work that can throw has been completed. // So now set the current node with the correct values. data = node.data; left = nL.release(); right = nR.release(); } |
我也可以建议从库boost(如果您可以使用它)共享ptr,而不是简单的指针吗?它可以解决很多您可能遇到的问题,比如指针无效、E.T.C.的深度复制。
Is this the correct way to implement the default constructor?
不,对未使用
在默认构造函数中设置指向
1 2 3 4 5 | TreeNode::TreeNode(){ data =""; //not required since data being a std::string is default initialized. left = NULL; right = NULL; } |
我看不出你的其他代码有这样的问题。赋值操作符shallow复制节点,而复制构造函数deep复制节点。
根据您的要求采取适当的方法。-)
编辑:
不要在默认构造函数中分配指针,而是使用初始化列表