据我所知,这些语义仅用于复制构造函数,移动构造函数,复制赋值,移动赋值和析构函数。使用= delete
是为了禁止使用其中一个函数,如果你想明确编译器在哪里使用这些函数的默认值,则使用= default
。
在上课时使用这些关键字的最佳做法是什么?或者更确切地说,在开发课程时如何注意这些?
例如,如果我不知道我是否会使用其中一种功能,最好是使用delete
禁止它还是允许它使用default
?< / p>
答案 0 :(得分:6)
好问题。
同样重要的是: 使用= default
和= delete
。
我对此有一些有争议的建议。它与我们所有学习(包括我自己)的C ++ 98/03相矛盾。
使用您的数据成员启动您的类声明:
class MyClass
{
std::unique_ptr<OtherClass> ptr_;
std::string name_;
std::vector<double> data_;
// ...
};
然后,尽可能接近,列出您要显式声明的所有六个特殊成员,并以可预测的顺序列出(并且不要列出您希望编译器处理的那些成员)。我更喜欢的顺序是:
// this tells me the very most important things about this class.
// I like to see my copy members together
// I like to see my move members together
此订单的原因是:
例如:
class MyClass
{
std::unique_ptr<OtherClass> ptr_;
std::string name_;
std::vector<double> data_;
public:
MyClass() = default;
MyClass(const MyClass& other);
MyClass& operator=(const MyClass& other);
MyClass(MyClass&&) = default;
MyClass& operator=(MyClass&&) = default;
// Other constructors...
// Other public member functions
// friend functions
// friend types
// private member functions
// ...
};
知道约定,人们可以很快看到,而不必检查~MyClass()
隐式默认的整个类声明,并且附近的数据成员,很容易看到编译器声明和提供的析构函数是什么确实
接下来我们可以看到MyClass
有一个显式默认的默认构造函数,并且附近声明了数据成员,很容易看到编译器提供的默认构造函数的作用。也很容易看到为什么已经显式声明了默认构造函数:因为我们需要一个用户定义的复制构造函数,如果没有显式默认,这将禁止编译器提供的默认构造函数。
接下来我们看到有一个用户提供的复制构造函数和复制赋值运算符。为什么?好吧,随着附近的数据成员,很容易推测可能需要unique_ptr ptr_
的深层副本。当然,我们无法在不检查复制成员定义的情况下知道这一点。但即使没有这些定义也很方便,我们已经非常了解情况。
对于用户声明的复制成员,如果我们什么都不做,则隐式不会移动成员。但是在这里我们很容易看到(因为所有内容都可预测地在MyClass
声明的顶部进行分组和排序),我们已明确默认移动成员。再次,因为数据成员在附近,我们可以立即看看这些编译器提供的移动成员将会做什么。
总之,我们还不知道MyClass
究竟做了什么以及它将在此计划中扮演什么角色。然而,即使缺乏这些知识,我们已经对MyClass
了解了很多。
我们知道MyClass
:
OtherClass
。ptr_
构建自己,空name_
和data_
。在10行左右的代码行中要知道很多。我们没有必要去寻找数百行代码,我确信这些代码是MyClass
正确实现所有这些所需要的:因为它全部位于顶部且位于可预测的顺序。
有人可能想要调整此配方,以便在数据成员之前放置嵌套类型,以便可以根据嵌套类型声明数据成员。但是,这一建议的精神是宣布私人数据成员和特殊成员,尽可能接近顶端,并尽可能接近彼此。这与过去给出的建议(可能甚至是我自己)相反,私有数据成员是一个实现细节,不足以成为类声明的顶端。
但事后看来(后见之明总是20/20),私有数据成员,即使远程代码无法访问(这是一件好事),做决定并描述一种类型的基本行为当任何特殊成员都是编译器提供的时候。 了解一个班级的特殊成员所做的事情,是了解任何类型的最重要方面之一。
每种类型都有这些问题的答案,最好是提出这些问题&amp;尽快回答的问题。然后你可以更容易地专注于使这种类型与其他类型不同的东西。
答案 1 :(得分:4)
此外,使用=default
而不是手动滚动保留了POD的性质,正如此处详细描述的那样:Default constructors and POD
答案 2 :(得分:1)
当您尝试维护rule of 5以确保特殊成员函数按照您的意图行事时,您经常会看到= default
,以便班级的读者可以看到您确实考虑过你希望这个功能如何表现。
如果您打算制作不可复制或不可移动的内容,则可以使用= delete
。虽然我也看到人们delete
是一个继承的函数,如果他们不希望那个特定的派生类具有这个功能,虽然我不是那个人的忠实粉丝,因为它往往指向糟糕的架构/设计