使用 = default 生成默认构造函数
在新标准下,如果我们想要一个默认构造函数,我们可以在参数列表的后面添加= default
来要求编译器为我们自动生成一个默认构造函数。= default
可以和类内部的声明或者类外部的定义一起出现。与任何其他函数一样,如果= default
出现在类体内,则默认构造函数将被内联;如果它出现在类外的定义中,则默认情况下不会内联。
Code:
class Sales_data{
Sales_data() = default; // the default constructor and is inlined
Sales_data(const std::string &s): bookNo(s) { }
private:
std::string bookNo;
};
如果= default
能工作,那么就要求类的成员变量必须具有默认的初始化器(如:内置类型int
,double
,string
等、其他具有默认构造函数的类。)。如果某个类的成员变量不支持默认初始化器对其进行默认初始化,那么必须显示编写构造函数来完成各个成员变量的初始化。
类类型(Class Type)成员的类内初始化器
下面我们以一个例子来说明。我们除了定义Screen
类,还将定义一个窗口管理器类Window_mgr
,它表示给定显示器上的Screen
集合。这个类将有一个Screen
向量,其中每个元素代表一个特定的Screen
对象。默认情况下,我们希望我们的Window_mgr
类有一个默认构造函数来实现对Screen
向量进行初始化。 在新标准下,最佳的实现方式是使用类内初始化器:
Code:
class Window_mgr {
private:
// Screens this Window_mgr is tracking
// by default, a Window_mgr has one standard sized blank Screen
std::vector screens{Screen(24, 80, ' ') };
};
当我们初始化类类型的成员时,我们在为该成员类型的构造函数提供参数。在这种情况下,我们使用具有单个元素的初始化器列表来初始化vector
成员。该初始化器列表包含一个Screen
值,该值传递给vector
构造函数以创建单元素向量。该值由Screen
构造函数创建,该构造函数采用两个大小参数和一个字符来创建给定大小的空白屏幕。
正如我们所看到的,类内初始化器必须使用初始化的形式(我们在初始化Screen
的数据成员时使用的方式)或使用花括号的初始化器列表形式(就像我们对screens
所做的那样)。
委托构造函数
新标准扩展了构造函数初始值设定项的使用,以便让我们定义所谓的委托构造函数。委托构造函数使用其自己的类中的另一个构造函数来执行其初始化。它将一些(或全部)工作“委托”给另一个构造函数。
与任何其他构造函数一样,委托构造函数具有成员初始化列表和函数体。在委托构造函数中,成员初始化列表具有单个条目,该条目是类本身的名称。与其他成员初始值设定项一样,该类的名称后跟带括号的参数列表。参数列表必须与类中的另一个构造函数匹配。
Code:
class Sales_data {
public:
// nondelegating constructor initializes members from corresponding arguments
Sales_data(std::string s, unsigned cnt, double price):
bookNo(s), units_sold(cnt), revenue(cnt*price) { }
// remaining constructors all delegate to another constructor
Sales_data(): Sales_data("", 0, 0) {}
Sales_data(std::string s): Sales_data(s, 0,0) {}
Sales_data(std::istream &is): Sales_data(){ read(is, *this); }
private:
std::string bookNo;
unsigned units_sold;
double revenue;
};
在Sales_data
中,除了一个构造函数之外的所有构造函数都委托他们的工作。第一个构造函数接受三个参数,使用这些参数初始化数据成员,并且没有进一步的工作。我们还定义了一个默认构造函数,该函数委托三参数的构造函数来实现自己的初始化,而且该构造函数也没有做其他的工作。接受字符串的构造函数也将自己的初始化工作委托给三参数版本。
接受参数std::istream
的构造函数也是委托构造函数。它将自己的初始化工作委托给默认构造函数,后者又委托给三参数构造函数。一旦这些构造函数完成它们的工作,就会运行istream
和构造函数的主体代码。它的构造函数体调用read
函数来读取给定的istream
。
当一个构造函数将初始化工作委托给另一个构造函数时,被委托的构造函数的初始化器列表和函数体都会运行。在Sales_data
中,被委托的构造函数的函数体刚好为空。如果委托构造函数的函数体中有代码,则该代码在被委托的构造函数返回之后才会运行。
constexpr构造函数
虽然构造函数不能是const
,但是文字类(literal class)中的构造函数可以是constexpr
函数。实际上,文字类必须提供至少一个constexpr
构造函数。
constexpr
构造函数可以以= default
的形式进行声明。然而constexpr
构造函数需要同时满足构造函数的条件(它可以没有return
语句)和constexpr
函数的条件(它可以拥有的唯一可执行语句是return
语句)。所以constexpr
构造函数的函数体通常是空的。我们通过在函数声明前加上关键字constexpr
来定义一个constexpr
构造函数。
Code:
class Debug {
public:
constexpr Debug(bool b = true): hw(b), io(b), other(b) { }
constexpr Debug(bool h, bool i, bool o):
hw(h), io(i), other(o) { }
constexpr bool any() { return hw || io || other; }
void set_io(bool b) { io = b; }
void set_hw(bool b) { hw = b; }
void set_other(bool b) { hw = b; }
private:
bool hw; // hardware errors other than IO errors
bool io; // IO errors
bool other; // other errors
};
constexpr
构造函数必须初始化每个数据成员。初始值设定项必须使用constexpr
构造函数或者是常量表达式。
constexpr
构造函数用于生成constexpr
类型的对象以及constexpr
函数中的参数或返回类型。
Code:
constexpr Debug io_sub(false, true, false); // debugging IO
if (io_sub.any()) // equivalent to if(true)
cerr << "print appropriate error messages" << endl;
constexpr Debug prod(false); // no debugging during production
if (prod.any()) // equivalent to if(false)
cerr << "print an error message" << endl;
参考文献
[1] Lippman S B , Josée Lajoie, Moo B E . C++ Primer (5th Edition)[J]. 2013.