C++构造函数(一)
本篇是介绍C++的构造函数的第一篇(共二篇),属于读书笔记,对C++进行一个系统的复习。
构造函数的概念和作用
全局变量未初始化时为0,局部变量未初始化时的值却是无法预测的。这是因为,全局变量的初始化是再程序装载时便一次性完成的,自动地初始化为零并不会有额外的开销,因此编译器默认初始化为零。但是对局部变量并没有这样的操作,需要程序去完成初始化。
基本类型变量的初始化只需要在定义时赋值就可以了,但是对于对象的初始化,情况就要复杂一些。比如,有的对象需要在创建时就令某个指针指向一块内存空间,这就涉及到空间的分配;再比如,有的对象包含文件操作,在创建时就需要打开特定文件。举这两个例子是要说明,对象的初始化,不仅仅是简单赋值那么简单。因此构造函数(constructor)是必要的,它的存在就是为了完成对象的初始化工作。
构造函数名字和类名一样,没有返回值,可以重载。可以有多个构造函数,如果没有就自动生成一个默认的构造函数,但是它什么也不做。
默认构造函数
自动生成的构造函数是一个没有参数的构造函数。但凡自己已经写了一个构造函数,都不会自动生成无参的构造函数。不论是程序员自己写的无参构造函数还是自动生成的无参构造函数,只要是无参构造函数,就称为默认构造函数。那么,考虑下面一种情况:程序员只写了有参的构造函数,这种情况下就不存在默认构造函数。所以默认构造函数不一定存在,或者说无参的构造函数不一定存在。为什么要强调无参的构造函数不一定存在,有没有哪种构造函数一定是存在的呢?复制构造函数是一定存在的,在后文介绍。
对象在生成的时候一定会调用某个构造函数初始化,而对象一旦生成,就再也不会在其上调用构造函数。
构造函数是负责内存的分配吗?不是的,它只负责初始化。所以创建一个对象的过程是,先内存分配(在堆或栈上),之后在分配好的内存上调用构造函数。
构造函数在数组中的使用
像 int a[3] = { 0 ,1 ,2 }; 这样的语句就实现了数组的初始化。如何给一个对象数组初始化呢,可以使用类似的方法。
1 class CNum { 2 3 int num; 4 public: 5 6 CNum(){ num = 0;} 7 CNum(int a){ num = a;} 8 CNum(int a, int b){ num = a + b;} 9 }
可以这样调用:
1 CNum n1[3]; //调用了三次无参构造函数,结果是{0,0,0} 2 CNum n2[3] = {0, 1, 2}; //调用了三次CNum(int a),结果是{0,1,2} 3 CNum n3[3] = {0, 1}; //调用了两次CNum(int a),和一次无参构造函数,结果是{0,1,0} 4 CNum n4[3] = {0, CNum(1,2), CNum(2,3)}; //调用了一次无参构造函数和两次CNum(int a, int b),结果是{0,3,5}
想在堆上生成数组可以这样:
1 CNum * ptrnum[3] = {new CNum(1,2), new CNum(2,3)};
这样生成的实际上是一个指针数组,前两个元素指向的对象使用了CNum(int a, int b)初始化,第三个元素没有初始化,是一个野指针(请避免这样做)。所以这一个语句实际上调用了两次构造函数。