为什么 没有缺省构造函数的类类型成员 必需要在初始化列表 里初始化 ?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了为什么 没有缺省构造函数的类类型成员 必需要在初始化列表 里初始化 ?相关的知识,希望对你有一定的参考价值。
名词解释
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; }
本文出自 “牛丽” 博客,请务必保留此出处http://15129279495.blog.51cto.com/10845420/1759816
以上是关于为什么 没有缺省构造函数的类类型成员 必需要在初始化列表 里初始化 ?的主要内容,如果未能解决你的问题,请参考以下文章