C++中各种初始化方式示例详解

2020-01-06 17:24:18丽君

对于书本中给出的示例:


string dots(10, '.'); //直接初始化
string s(dots);      //直接初始化

这里s的初始化书本说是直接初始化,看起来似乎像是拷贝初始化,其实的确是直接初始化,因为直接初始化是用参数来直接匹配某一个构造函数,而拷贝构造函数和其他构造函数形成了重载,以至于刚好调用了拷贝构造函数。

事实上,C++语言标准规定复制初始化应该是先调用对应的构造函数创建一个临时对象,然后拷贝构造函数再将构造的临时对象拷贝给要创建的对象。例如:


string a = "hello";

上面代码中,因为“hello"的类型是const char *,所以string类的string(const char *)构造函数会被首先调用,创建一个临时对象,然后拷贝构造函数将这个临时对象复制到a。但是标准还规定,为了提高效率,允许编译器跳过创建临时对象这一步,直接调用构造函数构造要创建的对象,从而忽略调用拷贝构造函数进行优化,这样就完全等价于直接初始化了,当然可以使用-fno-elide-constructors选项来禁用优化。

如果我们将string类型的拷贝构造函数定义为private或者定义为delete,那么就无法通过编译,虽然能够进行优化省略拷贝构造函数的调用,但是拷贝构造函数在语法上还是要能正常访问的,这也是为什么C++ primer第五版第13章拷贝控制13.1.1节末尾442页最后一段话中说:

“即使编译器略过了拷贝/移动构造函数,但在这个程序点上,拷贝/移动构造函数必须是存在且可访问的(例如,不能是priviate的)。

拷贝初始化不仅在使用=定义变量时会发生,在以下几种特殊情况中也会发生:

1.将一个对象作为实参传递给一个非引用的形参;

2.从一个返回类型为非引用的函数返回一个对象;

3.用花括号列表初始化一个数组中的元素或一个聚合类中的成员。

其实还有一个情况,比如:当以值抛出或捕获一个异常时。

另外还有比较让人迷惑的地方在于vector<string> v2(10),在《C++ Primer 5th》中说这是值初始化的方式,但是仔细看书本,这里的值初始化指的是容器中string元素,也就是说v2本身是直接初始化的,而v2中的10个string元素,由于没有给出初始值,因此标准库对容器中的元素采用了值初始化的方式进行初始化。

结合来说:

只要使用了括号(圆括号或花括号)但没有给出具体初始值,就是值初始化。可以简单理解为括号告诉编译器你希望该对象初始化。

没有使用括号,就是默认初始化。可以简单理解成,你放任不管,允许编译器使用默认行为。通常这是糟糕的行为,除非你真的懂自己在干什么。