有关于各种初始化的一些理解
在CPP primer这本书中,在前三章就已经提到了若干个与初始化有关的术语。
默认初始化、值初始化、列表初始化、直接初始化、拷贝初始化。
在刚开始学习的时候由于还没有类相关的知识的铺垫,因此只能使用内置类型为例来理解这几个“初始化”的含义,但我是在学习完第七章类之后才对这几个初始化开始有了一点自己的理解(当然现在的理解也不一定正确,学习的过程不就是不断推翻以前的认知建立新的理解的过程吗)。
以下所讲的初始化暂时不涉及引用,就以一般的数值类型和简单的类类型如Sales_data这种来理解。
默认初始化
分两大类,内置数值类型和类类型。
1. 对于内置数值类型来说,默认初始化取决于变量的存储类型(主要是存储期限和作用域)。对于静态变量(文件作用域变量和局部静态变量),默认初始化为0。而局部自动变量的默认初始化就是不初始化,里面是垃圾值。
2. 对于类类型来说,一个类类型对象想要被默认初始化首要的前提就是这个类类型具有默认构造函数。在创建一个类类型对象时不显式提供任何初始值,则这个类类型对象就会被默认初始化。对于不具有默认构造函数的类类型,不允许以默认初始化方式定义该类型的变量。
值初始化
也分两大类,内置数值类型和类类型。
内置数值类型的对象被值初始化时被初始化为0。而类类型的对象的值初始化和默认初始化是一样的。
至于什么情况下会执行默认初始化,什么时候执行值初始化这个翻翻书就知道了。
以上讲的都是在创建对象时不显式提供初始值的两种情形。
直接初始化和拷贝初始化是一定要有初始值的,也是我个人认为最难理解的点,因为需要有类的构造函数以及单实参构造函数和类的隐式类型转换机制的相关知识才能比较好理解。(以下的解释针对类类型)
直接初始化
直接初始化可以理解为根据初始值作为实参直接调用构造函数来创建对象的方式。
拷贝初始化
对一个类类型对象执行拷贝初始化的前提是首先得有一个相同类型的对象来作为拷贝对象。
刚看这本书的时候一度很纳闷,书上说了,使用等号=的初始化方式是拷贝初始化,但是又允许string s = “hello”; 等号右边不是一个string对象,为什么能拷贝给s。
同时书上又说了string s = “hello”; 等价与string s(“hello”),这下子更迷糊了,两者的区别到底是什么。
再后来学到类类型的隐式转换之后就会知道,实际上拷贝初始化一个string对象必定需要另一个string对象这个说法没毛病,string s = “hello”; 这条定义语句语法正确,是因为编译器背后默默地执行了一次隐式转换,以一个字符串字面值const char *创建了一个临时string对象,这个临时string对象才是真正用于拷贝初始化s的初始值。
而const char *之所以能隐式转换成string对象,不难推断,标准库string类型必定定义了一个形参为const char *的string的构造函数,且这个构造函数不为explicit。
特别标注这两种初始化方式的不同主要是方便我后面做笔记。 在看到后面顺序容器一章时,也谈到了初始化的话题,例如用一个容器对象去拷贝初始化另一个容器对象,或者以一对迭代器范围来初始化一个容器对象。比较特别的是,以一对迭代器范围来初始化一个容器对象时,那一对迭代器所属的容器类型可以和被初始化的容器类型不一致,包括容器模板类型不一致,甚至元素类型都可以不一致,只要求迭代器所指的元素的类型能够转换成被初始化的容器的元素类型(注意,这里的“能转换”不一定指的是隐式转换)。那么我不禁思考:这种用迭代器范围所包含的元素(又或者通过花括号括起来的初始值列表)来初始化新容器中的元素,是什么初始化方式?什么样的值能作为初始值?
为了分析这个问题,我和上一篇文章的做法一样,对于myComplxe类的那个单实参的
myComplex(double x):re_(x){}
构造函数处分析带explicit和不带explicit的两种情况。
最终个人得出的结论是:
1. 使用迭代器范围中的元素去初始化容器对象时,是使用迭代器所指的元素的值去直接初始化新容器中的元素(因为myComplex(double)这个构造函数带不带explicit都能成功初始化);
1 explicit myComplex(double x):re_(x){} 2 3 vector<double> dvec = {1.0, 2.0, 3.0}; 4 vector<myComplex> vec1(dvec.begin(),dvec.end());
2. 相反,通过初始值列表来初始化容器对象时,是使用初始值列表中的初始值去拷贝初始化新容器中的元素(因为当myComplex(double)带explicit时初始化语句语法错误,而不带explicit时正确,编译器背后将double的列表隐式转化为了myComplex的列表)
explicit myComplex(double x):re_(x){} //下面的定义报错,没有与参数列表匹配的构造函数 vector<myComplex> vec2{4.0,5.0,6.0};
myComplex(double x):re_(x){} //成功初始化了 vector<myComplex> vec2{4.0,5.0,6.0};
现在的理解很可能是片面的,因为有关于 构造函数的知识还没有学完,等学到到后面第13、14章时可能又会推翻现有的认知了。

浙公网安备 33010602011771号