4 回答
TA贡献1880条经验 获得超4个赞
我要说三法则成为三,四,五的规则:
每个类应明确定义以下一组特殊成员函数:
没有
析构函数,复制构造函数,复制赋值运算符
此外,显式定义析构函数的每个类可以显式定义移动构造函数和/或移动赋值运算符。
通常,以下一组特殊成员函数是明智的:
无(对于许多隐式生成的特殊成员函数正确且快速的简单类)
析构函数,复制构造函数,复制赋值运算符(在这种情况下,类不可移动)
析构函数,移动构造函数,移动赋值运算符(在这种情况下,该类将不可复制,对于底层资源不可复制的资源管理类很有用)
析构函数,复制构造函数,复制赋值运算符,移动构造函数(因为复制省略,如果复制赋值运算符按值获取其参数,则没有开销)
析构函数,复制构造函数,复制赋值运算符,移动构造函数,移动赋值运算符
请注意,不会为显式声明任何其他特殊成员函数的类生成移动构造函数和移动赋值运算符,不会为显式声明移动构造函数或移动的类生成复制构造函数和复制赋值运算符赋值运算符,并且具有显式声明的析构函数和隐式定义的复制构造函数或隐式定义的复制赋值运算符的类被视为已弃用。特别是,以下完全有效的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使用移动构造函数并移动赋值运算符(如果可用),并且应该足够快。
不用于资源管理的类(即,没有非空的析构函数)或子类型多态(即,没有虚拟析构函数)应该不声明五个特殊成员函数; 它们都将自动生成并且行为正确且快速。
TA贡献1834条经验 获得超8个赞
是的,我认为为这些类提供移动构造函数会很好,但请记住:
这只是一种优化。
仅实现一个或两个复制构造函数,赋值运算符或析构函数可能会导致错误,而没有移动构造函数可能会降低性能。
无需修改即可始终应用移动构造函数。
有些类总是分配它们的指针,因此这些类总是在析构函数中删除它们的指针。在这些情况下,您需要添加额外的检查,以确定其指针是已分配还是已被移走(现在为空)。
- 4 回答
- 0 关注
- 599 浏览
添加回答
举报