【问题标题】:Constructor and const reference构造函数和常量引用
【发布时间】:2015-08-27 21:18:04
【问题描述】:

我现在正在学习 C++(大约 2 天前开始),我在编写 Node.js 的 Copy C'tor 时遇到了一些麻烦。 Node 是一个类如下:

template <class T>
class Node {
        T* data;
        Node<T>* next;
        friend class Iterator<T>;
    public:
        Node():data(NULL),next(NULL){}
        Node(const T& data):data(NULL),next(NULL){
                T* copy = new T(data);
                this->data = copy;
        }

        Node(const Node& node):data(NULL),next(NULL){

            Node<T> dummy;
            dummy.data = node.data;
            dummy.next = node.next;
            Node<T>* head=new Node(*dummy);
            *this = *head;
            while(dummy.next != NULL) {
                dummy = *(dummy.next);
                head = head->next;
                head = new Node(*dummy);
            }
        }

注意:我有 operator* 所以 *dummy 结果为 T 类型。

另一个注意事项:我的公共和私有字段可能是错误的 - 但我稍后会处理它。

你吐了一点之后,让我们看看Copy C'tor。

它获得了 Node 的 const 引用,然后我尝试创建一个指向它的指针。编译器输出错误: Node&lt;T&gt;* dummy= &amp;node; 结果invalid conversion from 'const Node&lt;int&gt;*' to 'Node&lt;int&gt;*'(我有一个简短的主要尝试创建Node&lt;int&gt;)。

好的,看来我无法创建指向 const 的指针,所以我尝试手动复制它的字段,如代码所示。 当我运行 Eclipse 调试器并检查它是否有效时 - 它确实有效。 但是,当我继续执行步骤时,会在 head 上调用 D'tor(在复制构造函数的末尾),结果一切都崩溃了。 所以我不知道下一步该做什么,或者即使我的方法是正确的。

我应该如何制作复制构造函数?我想我明白为什么要调用 D'tor(我创造了一些东西,在块的末尾,一些东西被破坏了 - 对吧?),但我不知道如何使它正确。

【问题讨论】:

  • 这里的主要问题是您似乎不清楚“复制节点”的确切含义。尝试先确定这一点。看起来这可能是链表中的一个节点。因此,只需复制它就会让您的两个节点指向同一个 "next" ,这会破坏列表结构。您的代码似乎试图通过克隆列表的整个其余部分来解决此问题。但是,然后您在下一个节点上调用复制构造函数(如果成功,它将复制整个节点),因此您实际上最终几乎是平方列表!另外,你浅拷贝data
  • 鉴于复制节点的含义不是很清楚,我的建议是禁用复制构造函数;并添加一个名为clone 的函数,它会复制所有节点和数据。你当然可以有一个移动构造函数和移动赋值运算符。

标签: c++ constructor nodes copy-constructor const-reference


【解决方案1】:

复制构造函数的目的是对传递的对象进行“精确复制”。因此,根据 datanext 指针的语义,您只需使用初始化列表来分配它们:

Node(const Node& node): data(node.data), next(node.next) {}

由于这就像预期的默认行为(复制成员),您可以简单地省略复制构造函数,编译器会默认生成一个合适的。

请注意,您绝对可以创建指向 const 对象的指针:编译器抱怨的是您的指针的类型声明在此过程中丢失了 const 位。

第二点:对于深层副本,您可以使用以下内容:

Node(const Node& node): 
    data(node.data == NULL? NULL: new T(*node.data)),
    next(node.next == NULL? NULL: new Node(*node.next)) {}

当然,在这种深拷贝场景中,您的容器正在获取成员字段的“所有权”(这引发了一个问题:为什么它们首先是指针?)因此应该小心正确地 delete 他们一个析构函数。

【讨论】:

  • 不,重写复制构造函数的目的是进行精确复制。根据您的建议,您复制data 指针。现在,Node 的哪个实例负责删除该指针?虽然示例中没有析构函数,但我假设这个类负责内存。
  • @paddy 我同意“精确”可能不是正确的词,但“功能等效”绝对是预期行为的一部分。我添加了一个深拷贝案例。从发布的代码来看,并不完全清楚预期的所有权语义是什么。
  • 它会创建一个副本吗?如果我稍后更改源节点,是否会在复制的节点中更改?我不想要那个。我想要一份完整的单独副本。
  • @SiuusSE 您需要注意复制指针和复制它指向的内存之间的区别。
  • @SiuusSE 在深拷贝案例中,您应该将 Node 视为“拥有”任何内容数据和 next 指向的黑盒子。
【解决方案2】:

您发布的代码与您引用的错误语句不完全匹配:

Node<T>* dummy= &node;

但无论如何,你可以这样做:

const Node<T>* dummy= &node;

因为nodeconst,所以任何指向它的引用或指针也必须是const。您也无法在 node 上调用非 const 方法,因此您为数据访问器创建的任何方法都需要是 const 正确的。

【讨论】:

  • 抱歉给我造成了混乱,认为发布相同的代码略有不同会令人困惑。无论如何,如果我将指针设为 const,我将如何前进到下一个节点?
  • 指针本身不是 const。这可能会令人困惑。它指向的对象是const,可以修改指针。如果是Node&lt;T&gt; * const dummy,则意味着指针不能更改,而是指向非常量对象的指针。最后,const Node&lt;T&gt; * const dummy 是一个指向 const 对象的 const 指针。欢迎使用 C++。 =)
  • 只是为了揭开上面的神秘面纱,const Node&lt;T&gt; 实际上是Node&lt;T&gt; const 的语法简写。如果你这样想,你需要记住的是,const 之前的东西是被设为 const 的东西。这适用于函数、数据类型,当然还有*
猜你喜欢
  • 2015-07-24
  • 2023-03-20
  • 2013-04-16
  • 2015-12-17
  • 2021-12-25
  • 1970-01-01
  • 2012-04-04
  • 1970-01-01
  • 2016-02-15
相关资源
最近更新 更多