C ++
因此,观看右值引用这个美好的演讲结束后,我认为每一个类将有利于这样的“移动构造”的, template<class T> MyClass(T&& other)
的编辑 ,当然是一个“移动赋值运算符”, template<class T> MyClass& operator=(T&& other)
正如Philipp在他的回答中指出的那样,如果它有动态分配的成员,或者通常存储指针。 就像你应该有一个copy-ctor,赋值运算符和析构函数,如果前面提到的点适用。 思考?
我会说三条法则成为三,四,五规则:
每个类都应该明确地定义下列一组特殊成员函数中的一个:
另外,每个明确定义析构函数的类都可以明确定义移动构造函数和/或移动赋值运算符。
通常,下列特殊成员函数之一是明智的:
请注意,移动构造函数和移动赋值运算符不会为显式声明任何其他特殊成员函数的类生成,将不会为显式声明移动构造函数或移动的类生成复制构造函数和复制赋值运算符赋值运算符,并且具有明确声明的析构函数和隐式定义的复制构造函数或隐式定义的复制赋值运算符的类将被视为弃用。 特别是,以下完美有效的C ++ 03多态基类
class C {
virtual ~C() { } // allow subtype polymorphism
};
应改写如下:
class C {
C(const C&) = default; // Copy constructor
C(C&&) = default; // Move constructor
C& operator=(const C&) & = default; // Copy assignment operator
C& operator=(C&&) & = default; // Move assignment operator
virtual ~C() { } // Destructor
};
有点烦人,但可能比替代(自动生成所有特殊成员函数)更好。
与三巨头的规则相反,如果不遵守规则会造成严重损害,则不明确声明移动构造函数和移动赋值运算符通常很好,但在效率方面往往不是最理想的。 如上所述,只有在没有显式声明的拷贝构造函数,拷贝赋值运算符或析构函数的情况下,才会生成移动构造函数和移动赋值运算符。 这与传统的C ++ 03行为在复制构造函数和复制赋值运算符的自动生成方面不是对称的,但更安全。 因此,定义移动构造函数和移动赋值运算符的可能性非常有用,并且创建了新的可能性(纯可移动类),但遵循三巨头C ++ 03规则的类仍然可以。
对于资源管理类,如果无法复制底层资源,则可以将复制构造函数和复制赋值运算符定义为已删除(将其视为定义)。 通常你仍然想要移动构造函数并移动赋值运算符。 复制和移动赋值操作符通常使用swap
来实现,就像在C ++ 03中一样。 如果你有一个移动构造函数和移动赋值运算符,专门化std::swap
将变得不重要,因为通用std::swap
使用移动构造函数和移动赋值运算符(如果可用),并且速度应该足够快。
不用于资源管理的类(即没有非空的析构函数)或子类型多态(即没有虚拟析构函数)应该声明五个特殊成员函数中的任何一个; 他们都将自动生成并且表现正确和快速。
我无法相信没有人与此相关。
基本上文章主张“零规则”。 引用整篇文章并不合适,但我相信这是主要观点:
具有自定义析构函数,复制/移动构造函数或复制/移动赋值运算符的类应该专门处理所有权。 其他类不应该具有自定义析构函数,复制/移动构造函数或复制/移动赋值运算符。
这一点也是恕我直言,重要的是:
标准库中包含常见的“包中所有权”类: std::unique_ptr
和std::shared_ptr
。 通过使用定制的删除对象,两者都已经变得足够灵活以管理几乎任何类型的资源。
我不这么认为,三条规则是一条经验法则,规定一个实现下列其中一个但不是全部的类可能是错误的。
然而,将移动构造函数或移动赋值运算符省略并不意味着错误。 这可能是优化时错过的机会(在大多数情况下),或者移动语义与这个类无关,但这不是一个错误。
尽管在相关时定义移动构造函数可能是最佳实践,但这不是强制性的。 在许多情况下,移动构造函数与类无关(例如std::complex
),并且所有在C ++ 03中正确行为的类将继续在C ++ 0x中正确行为,即使它们不定义一个移动构造函数。
上一篇: c++