名词解释

    1、缺省构造函数:类的构造函数无参或参数默认值统称为缺省构造函数。


    2、初始化列表:与其它函数不同,构造函数除了有名字,参数列表和函数体外还可以有初始化列表。列表以冒号开始后跟以逗号隔开的初始化字段。类成员是在构造函数的初始化列表创建好的,在创建类成员的同时,给成员变量一个初始化值。




在解释原因之前,需要了解构造函数是如何执行的

构造函数的执行过程

    首先,构造函数的执行分为三步。先创建函数的形参(如果没有形参这步可以省略),然后执行初始化列表(即使没有初试化列表),最后执行函数体的内容。


    没有初始化列表时,系统在创建类成员时会给成员一个初值。根据对象的作用域不同,初值也不同。代码证明请见附录1                                           

    (1)全局的对象和局部的static对象,系统会对 int类型的成员赋初值0,指针类型的成        员赋初值0x00000000。

    (2)除static类型外的局部对象,系统对成员赋一个随机值。

                                                                                                            

在执行函数体时,类成员已被创建好,并且有一个初值。函数体内给成员值时不是初始化,而是赋值。

可以通过下面的代码证明证明这一问题。

下面先给类B一个默认参数,这样就不需要在A的构造函数的初始化列表里初始化B类型的A成员。

                                                                                        

#include <iostream>
#include <cstdlib>

using namespace std;

class B
{
public:
	B(int  data=0)
		:_data(data)
	{
		cout << "B()" << endl;
		
	}
	
	B(const B &b)
		:_data(b._data)
	{
		cout << "B(const B &)" << endl;
	}


	B & operator=(const B &b)
	{
		cout << "operator=" << endl;
		_data = b._data;
		return *this;
	}

	~B()
	{
		cout << "~B()" << endl;
	}
	
private:
	int _data;
};


class A
{
public:
	A(B data = 1)
	{
		b = data;
		cout << "A()" << endl;
	}

	~A()
	{
		cout << "~A()" << endl;
	}
	
private:
	B b;
};


int main()
{
	{
		A a(2);
	}

	system("pause");
	return 0;
}



上述代码进行调试时,通过this指针和窗口可以看到main函数内是这样执行的:

1、用类A创建对象a,调用A的构造函数。

    执行类A的构造函数分三步。

    (1) 为构造函数的形参开辟空间,因形参时类B类型的对象,故创建形参时要调用B的构造函数

    (2) 执行A的构造函数的初始化列表(即使没有初始化列表),创建对象a的成员b,并赋初值。因成员b又是一个类类型的对象,创建b时会调用B的构造函数。B的构造函数执行完后,到此,A的构造函数的初始化列表已执行完毕。

    (3) 执行A构造函数的函数体。执行b=data,调用赋值运算符重载函数 ;然后执行cout <<"A()"<<endl;

因形参时构造函数内的对象,在执行完构造函数时要调用形参data的析构函数。到此,构造函数执行完毕。


2、对象a创建好后,要出a的作用域,调用A的析构函数,并且a的成员b也要调用B的析构函数。


3、最后执行完程序屏幕输出的内容如下:


为什么 没有缺省构造函数的类类型成员 必需要在初始化列表 里初始化 ?_初始化




为什么没有缺省构造函数的类类型成员要在初始化列表初始化


下面的代码中,类B没有缺省的构造函数,在类A中有一个类B类型的成员

#include <iostream>
#include <cstdlib>

using namespace std;

class B
{
public:
	B(int data)
	{}

private:
	int _data;
};


class A
{
public:
	A(int data=0)
	{}
private:
	B _b;

};
int main()
{

	A a;
	system("pause");
	return 0;
}


上述代码在执行时会报错。这是因为:

    在main() 中创建A类型对象a时要执行的构造函数,先创建形参,然后执行初始化列表。

    那么问题来了,初始化列表中要创建a的成员b并给b一个初值,那么就要调用B的构造函数。这时,因为已有构造函数系统不会生成默认的构造函数,而已写的构造函数必需要一个参数,此时初始化列表中并没有可以提供给B的构造函数的参数,故创建b时会出错。



必需在初始化列表初始化的成员还有:

     ☆常量成员

     引用类型成员变量。


    有了上面的基础后这就很好理解,初始化列表中要创建类成员并赋初值,而常量和引用类型的变量必需在创建时给一个值,常量在创建好后不能改变,引用必需在创建时初始化




附录1:

系统默认构造函数,给类对象的成员赋初值的情况

#include <iostream>
#include <cstdlib>

using namespace std;

class Test
{
public:
	void Display()
	{
		cout <<"_p = "<< _p << endl; 
		cout<<"_data = "<< _data << endl<<endl;
	}
private:
	int * _p;
	int _data;
};

Test test1;

int main()
{
	Test test2;
	static Test test3;

	cout << "test1 : "<<endl;
	test1.Display();

	cout << "test2 :"<<endl;
	test2.Display();

	cout << "test.3 :"<<endl;
	test3.Display();

	system("pause");
	return 0;
}


为什么 没有缺省构造函数的类类型成员 必需要在初始化列表 里初始化 ?_初始化_02