C++ 类: one 'reset' 如何在移动构造函数中创建模板字段?

C++ classes: How does one 'reset' a template field in the move constructor?

我是初学者,我希望我不会因为不懂术语而重复发帖,但我搜索了 Stack Overflow 和 Google 仍然没有找到我的答案问题,或者至少是我能理解的答案。

我正在为单链表作业编写节点 class。 class 有两个字段:elem 和 next。 Elem 是模板化的,next 是一个 Node 指针。

我决定编写一个自定义构造函数(以及所有其他构造函数),这样我就可以确定 next 被每个新节点初始化为 nullptr,所以我不会 运行 陷入困境指针问题。 class header:

template <typename E>
class Node {
public:

    //Constructors
    Node();
    Node( const Node<E> &other);           //copy
    Node<E>& operator=( Node<E> other );   //copy asn
    Node( Node&& other );                  //move
    Node<E>& operator=( Node<E>&& other ); //move asn
    ~Node();

    // {...Getters and setters and the like...}

private:
    E elem{};       //datatype independent element
    Node<E>* next;  //pointer to next list item
};

我的问题是编写移动构造函数。据我了解,移动构造函数的基本轮廓如下:

myClass( myClass&& otherMyClassObj ) {

    // Step 1: Steal object's resources

    myClassField = otherMyClassObj.myClassField;
    myClassOtherField = otherMyClassObj.myClassOtherField;
    /* ...etc... */

    // Step 2: Set other object to a valid but default state so it will be deleted

    otherMyClassObj.myClassField = <whatever is default for type>;
    otherMyClassObj.myClassOtherField = <whatever is default for type>;
    /*...etc...*/
}

当我尝试应用此大纲在我的 class 中编写移动构造函数时,我 运行 遇到了一个问题:elem 的默认状态是什么,我不知道它的类型知道是因为它是在运行时间确定的吗?我的移动构造函数代码:

template <typename E>
Node::Node( Node&& other ) {

    next = other.next;
    elem = other.elem;

    other.next = nullptr;
    other.elem = /* ???? */
}

我能想到的唯一解决方案是手动调用 other 上的析构函数,但这似乎草率且错误。我不能使用 nullptr,因为 elem 不一定是指针,但我不能使用 0,因为它 可能 是一个。我考虑过的另一种可能性是以某种方式直接在 elem 上调用 std::move(),但我不确定该怎么做,而且在移动构造函数中调用移动构造函数似乎本质上是错误的。

我真的很感激在这个问题上的一些帮助。请不要提出非标准的图书馆建议——我很乐意使用它们,但我的教授对我们使用哪些图书馆非常严格。

移动每个成员即可。

template <typename E>
Node::Node<E>( Node<E> && other ) : next(std::move(other.next)), elem(std::move(other.elem))
{
}

请注意,我不确定上面的确切模板语法。

遵守五规则是好事,但如果你不想在某些构造函数、赋值运算符或析构函数中做任何特殊的事情,那么考虑将它们设为 = default。这让您的事情变得更轻松、更安全。

我们对 E 的唯一了解是 default c-tor,否则代码无法编译。

一般来说,c-tor 的移动看起来像 Werner Henze 建议的那样。不过我建议你也重置指针。

template <typename E>
Node::Node<E>( Node<E> && other ) :
               next(std::move(other.next)),   // line 1
               elem(std::move(other.elem)){   // line 2
        // change to NULL for pre C++11
        other.next = nullptr;  // line 3
}

这是怎么回事?

解释如下:

  • 第 1 行 - 我们移动指针。指针不能真正移动。它被复制了。不过保持std::move是个好习惯,只为"show the intention"你感动。
  • 第 2 行 - 我们移动数据。这里有可能性:
    • 如果Emovableclass,这步class。
    • 如果E不是move-ableclass,而是copy-able(最简单的例子是int),复制
    • 如果 E 不是 move-able class,也不是 copy-able。这不会编译。

第2行执行后,E处于valid状态,但不能使用,因为里面可能有不同的数据。您可以销毁它或分配它。

  • 第 2 行 - 这里我们重置指针,因为 d-tor 可能决定用它做一些事情。

备选

最后我会给你 move c-tor 的替代代码 - 使用 swap.

它可能会更慢,但出于教育目的是可以的。

template <typename E>
Node::Node<E>( Node<E> && other ){
      // "this" class is created now.

      using std::swap;
      swap(next, other.next);
      swap(elem, elem);
}

最后的注释:

这里不需要{}:

private:
    E elem{};       //datatype independent element

这就够了:

private:
    E elem;       //datatype independent element