(3)组装复制构造函数
对,我们现在学习了,默认构造函数(没有参数的,可能是系统定义,可能是用户定义,系统只有在没有任何构造函数的情况下定义默认构造函数),而系统定义的默认构造函数就叫做组装默认构造函数,还有一种特殊的构造函数——类型转换构造函数(其实并不特殊);现在学习了组装复制构造函数(只要用户没有主动构造一个复制构造函数(使用类型引用做参数),系统就会自行组装);就算只定义了复制构造函数,系统也不会自动组装默认构造函数,所以,如果你定义了复制构造函数,那么一定要定义普通构造函数(最好有默认构造函数)。要不然,就没有构造函数了。
想到这里,我们发现不管什么什么类型的构造函数,功能都是实例并初始化对象,可能复制构造函数与普通构造函数的过程有些不同(其实就是使用的方法和领域不同),但是他们仍然是平等的。所以只有一个默认构造函数,并且只有当没有任何自定一构造函数的时候,系统才会有组装构造函数;而复制构造函数,是一定有的(不管在什么情况)。
其实复制构造函数的本质依然是构造函数,它的功能就是使用那个已知的对象中的元素,“逐个”的赋值给那个需要初始化的对象。所以,你可以把组装复制构造函数,想象成一个带有所有对象元素(顺序也一至)的初始化列表的构造函数。
与普通构造函数一样,涉及到对某个元素的初始化时,对于内建类型,直接使用copy的方法,对于类类型,使用定义复制构建函数。如果没有定义复制构建函数,就使用组装的,毕竟复制构建函数的参数是一定的,所以基本不存在无法复制的元素的可能。但是直接构造函数就有可能出现,子元素没有默认构造函数的情况,而不能进行构造,并且直接构造的时内建类型也有可能不初始化(依编译器而定)。
另外一个要注意的是,虽然我们没有办法进行复制初始化数组(这就是为什么数组没有办法作为参数传递,或则作为返回值返回,或者定义的时候用一个数组来复制初始化另外一个数组),但是当数组在某个其他类型的里面的是后,这个时候如果发生了复制初始化,作为元素的数组会也被复制初始化,但是,是通过逐个复制元素的方法。
(4)自定义复制构造函数
class Foo{
public:
Foo(); //默认构造函数
Foo(const Foo&); //复制构造函数
}
其中,const可以不写(但通常建议这么做),由于复制的作用要用到传递参数、返回值,这些都是隐式的调用,所以一定不能将其声明为explicit。所以可以知道explicit的作用就是承认在可能的情况下,系统默认在需要使用该函数的情况下,能不能自动使用。
从前面我们说可以将组装复制构造函数用相应的带有完全初始化列表的构造函数来代替,我们可以发现,复制构造函数要实现的功能基本稳定,所以通常组装复制构造函数基本可以满足要求。
所以我们经常不怎么自己定义复制构造函数,但是有些时候程序实现要求我们必须自定义构造函数,这个时候我们就表明了,构建复制构造函数的困难之处,不在于语法,它与普通的构造函数是一样的;关键是在,构建这个东西的用途,当用途明确后,其他就简单了。
有些时候,例如
1)对象成员是一个指向某个资源的指针(用户本意不想只是复制指针,那表明没有复制指针指向的对象),
2)或者该类型规定每新建一个对象需要做一些动作,那么这个时候就需要自定义复制构造函数
(注意哦!与普通构造函数一样,构造函数理论上的功能包括空间分配、元素初始化,以及相关处理,那个复制初始化符号“=”左右应该看成一个整体。)
3)或者该类型的每个对象都富有一个唯一ID成员的机制
(5)如何阻止类的复制初始化功能
1)我们知道,我们使用explicity可以声明,该复制构造函数不能被隐式使用,于是在参数、返回值的那些地方都不能用,但是如何让普通的复制初始化也不用呢?那就是声明为private。我们说过。构造函数的特殊还在于,它是直接被外层使用的,不需要套一个什么类的帽子(因为它就是类名),所以如果申明为private那么就不能使用了,这就是为什么大部分的构造函数都被申明为public了。