《c++ primer》7.5章节
《c++程序设计语言 第四版》16.2.6章节
1、成员是const或引用的必须初始化。
2、默认构造函数进行成员初始化的顺序和它们在类定义中出现的顺序一致。
3、explicit关键字修饰构造函数:
explicit是专门用来修饰构造函数的,作用是禁止构造函数的隐式转换。
什么是构造函数的隐式转换?来看一个例子:
struct ceshi
{
ceshi(int frist = 0,int second = 0)
:frist(frist),second(second)
{
}
int frist;
int second;
};
void showValue(const ceshi & c)
{
qDebug()<<c.frist<<c.second;
}
#define debug qDebug()<<
int main(int argc, char *argv[])
{
showValue(3);
}
构造函数的隐式转换有个条件:可以只传入一个实参。如上面定义的构造函数。
当向showValue()传入一个实参的时候,会隐式地调用构造函数ceshi(3),构造一个临时对象。
如果构造函数加上了explicit就禁止这么做了,必须显示地构造对象:
struct ceshi
{
explicit ceshi(int frist = 0,int second = 0)
:frist(frist),second(second)
{
}
int frist;
int second;
};
void showValue(const ceshi & c)
{
qDebug()<<c.frist<<c.second;
}
#define debug qDebug()<<
int main(int argc, char *argv[])
{
showValue(ceshi(3));
}
explicit禁止了编译器执行非预期的类型转换,故应尽可能使用之。
4、用 = 进行初始化默认是拷贝初始化,一般地初始化器生成的副本会被放入带初始化的对象,但若初始化器是个右值,这种拷贝可能被优化掉而采用移动操作,省略 = 会将初始化变为显示初始化(不允许隐私转换)(int a{66};)。
5、聚合类
如果一个类满足以下条件,那么可以称之为聚合类:
- 所有成员都是public的;
- 没有定义构造函数;
- 没有基类;
- 没有virtual函数。
聚合类可以用花括号初始化:
struct ceshi
{
int frist = 8;
int second;
};
int main(int argc, char *argv[])
{
ceshi c = {2,8};
}
初始化顺序必须和声明的顺序一致。
6、基类与构造函数
构造函数依次执行:
- 调用基类构造函数;
- 调用成员的构造函数
- 执行自身的构造函数函数体
析构函数与之相反,依次执行:
- 执行自身析构函数函数体
- 调用成员的析构函数
- 调用基类的析构函数
这样的顺序可以保证一个类成员在构造函数/析构函数中要操作它的时候它是存在的。
7、默认构造函数
不带参数的构造函数就是默认构造函数。
如果构造对象时未指定参数或提供了一个空的初始化器列表则会调用默认构造函数:
int a;
int a{};
一个接受默认参数的构造函数也可能是默认构造函数:
class A
{
public:
A(int i = 0)
{
}
};
8、初始化器列表std::initializer_list作为构造函数的参数:
#define debug qDebug()<<
struct ceshi
{
ceshi(int i = 88):frist(i)
{
debug "默认构造函数";
}
ceshi(std::initializer_list<int> list)
{
if(list.size() > 0)
{
frist = *list.begin();
}
debug "初始化器列表构造";
}
int frist;
};
int main(int argc, char *argv[])
{
ceshi c1{555};
debug c1.frist;
debug "---------";
ceshi c2{};
debug c2.frist;
}
当默认构造函数和初始化器列表构造函数都存在时,空初始化器{}优先匹配默认构造函数,非空初始化器{555}默认优先匹配初始化器列表构造函数。
9、构造对象时,初始化对象构造相比于“=”赋值操作有一些性能上的优势,因为赋值要构造临时的对象再付给待赋值的对象。
10、默认构造函数初始化数据规则:
- 如果成员存在类内初始值,用它初始化成员。
- 否则,使用默认初始化值初始化成员。