隐式定义vs明确声明的构造函数

时间:2017-12-31 20:12:09

标签: c++ c++11 constructor copy-constructor

隐式定义和显式声明的默认/复制构造函数有什么区别? 明确宣布

    struct road{
     std::string id;
     std::string type;
     std::vector<int> nodes;
     road() = default;
     road(const & road c_road) = default;
     road(road && m_road);
   };

隐式定义

struct road{
 std::string id;
 std::string type;
 std::vector<int> nodes;
 road(road && m_road);
};

与定义我自己的构造函数(如

)有什么区别
road::road(){}

road::road(const road & c_road):id(c_road.id)), type(c_road.type)),
  nodes(c_road.nodes){}

我的问题是,我是否需要显式声明默认的构造函数(= default; version),还是应该依赖隐式的?任何版本的版本都更快或更安全吗?任何版本都是“错误的”吗?

2 个答案:

答案 0 :(得分:1)

您可能希望查看&#34;零规则&#34;。您的类不需要构造函数,因为您的类将执行成员副本,您的成员已经为其定义了复制/移动构造函数。如果您必须管理资源或处理棘手的类型,那将是一个不同的故事。理想情况下,您的课程将如下所示:

struct road{
  std::string id;
  std::string type;
  std::vector<int> nodes;
};

如果用户声明了复制构造函数但省略了移动构造函数,编译器将为您生成移动构造函数。所以你需要这三个。同样,如果您决定实现赋值运算符,则需要复制赋值/移动赋值。这分别称为3/5规则。保持简单,只定义你需要的东西。

答案 1 :(得分:0)

明确违约(内联)与隐含违约:一般而言,这里的差别很小。它主要是一种风格选择。就个人而言,对于小班级(&lt; 1屏幕),我可能不会烦恼,因为很容易看到他们没有在任何地方定义,并且它为班级增加了一堆长度。

对于更长,更重要的课程,明白是很好的。这将立即告诉用户它是否具有值语义(至少是可移动的)或身份语义(不可移动),是否可复制,是否可以是不可变的(在这种情况下它是不可分配的)等等。

一个真正的区别是,如果您确实声明了某些特殊成员,则不会隐式生成其他成员。最常见的示例是您必须自己实现复制构造函数/赋值。这将禁用自动生成移动构造函数/赋值。所以你必须明确默认它们(如果你想要默认值)。

许多人忽略了另一个重要的区别:如果你的类被分为.h和.cpp文件(因为大多数非模板类应该是),这两种方法都会导致所有代码都生成在头文件中。这会减慢编译速度。所以第三种方法是这样做:

// .h file
struct Foo {
    Foo(const Foo&); // declare, but not define
};

// .cpp
Foo::Foo(const Foo&) = default;

这仍然利用了生成的成员,但将.cpp中的代码保存在您想要的位置。因此,对于大型非模板类,您通常只需要=delete或声明内容,而不是=default或者隐式生成它们!

至于定义自己的特殊成员:如果默认值有效,则不应自行定义特殊成员。你应该尝试使默认值工作。当您自己定义构造函数时,您必须经常提及每个成员一次。如果您添加或删除成员,这将导致维护工作,包括一些可以被静默忘记的工作。参见零度规则:http://www.nirfriedman.com/2015/06/27/cpp-rule-of-zero/

一个特例:默认构造函数。如果您有任何其他构造函数,则不会隐式生成它。然后,如果你想要它,它似乎同样容易=default它,并简单地定义它。这些都不一样。特别是,后者仍然被视为用户定义的构造函数,这意味着例如该类永远不会被视为trivially_constructible,因此不会被trivial,因此不会是{{} 1}}。这在某些应用中很重要。