我们思考下这个问题:如何初始化父类成员?父类构造函数和子类构造函数有何关系呢?在子类中可以定义构造函数,子类构造函数必须对继承而来的成员进行初始化:a> 直接通过初始化列表或者赋值的方式进行初始化;b> 调用父类构造函数进行初始化。
下来我们来说说父类构造函数在子类中的调用方式,分为两种:a> 默认调用:适用于无参构造函数和使用默认参数的构造函数;b> 显示调用:通过初始化列表进行调用,适用于所有父类构造函数。那么隐式调用是在子类的构造函数中啥都不加,显示调用时在子类构造函数后加上父类构造函数,如下所示
下来我们就对子类的构造函数一探究竟
#include <iostream> #include <string> using namespace std; class Parent { public: Parent(string s) { cout << "Parent(string s): " << s << endl; } }; class Child : public Parent { public: Child() { cout << "Child()" << endl; } Child(string s) : Parent(s) { cout << "Child(string s): " << s << endl; } }; int main() { Child c; Child cc("cc"); return 0; }
我们先来分析下,在子类 Child 中,它定义的第一个构造函数显然是隐式调用父类的构造函数。但是在父类的构造函数中,我们既没有定义无参构造函数,也没有定义默认参数的构造函数,所以这个隐式调用肯定会出错。而第二个对象 cc 是进行显示调用的,所以它不会报错。我们来看看编译结果
它报错说第 19 行出错,也就是子类的隐调用出错了,下来我们在父类中通过一个无参构造函数,来看看编译是否还会出错呢
我们看到编译通过了,并且也完美运行。我们来说说子类对象的构造规则:a> 子类对象在创建时会首先调用父类的构造函数;b> 先执行父类的构造函数再执行子类的构造函数;c> 父类构造函数可以被隐式调用或者显示调用。那么子类对象的创建时构造函数的调用又有什么顺序呢?1、调用父类的构造函数;2、调用成员变量的构造函数;3、调用类自身的构造函数。对此,有唐长老总结的一个口诀心法:先父母,后客人,再自己。
下来我们通过编程来看看子类创建时构造函数的执行顺序
#include <iostream> #include <string> using namespace std; class Object { string ms; public: Object(string s) { ms = s; cout << "Object(string s): " << ms << endl; } }; class Parent : public Object { string ms; public: Parent() : Object("Default") { ms = "Default"; cout << "Parent()" << endl; } Parent(string s) : Object(s) { ms = s; cout << "Parent(string s): " << s << endl; } }; class Child : public Parent { Object mOb1; Object mOb2; string ms; public: Child() : mOb1("Default 1"), mOb2("Default 2") { ms = "Default"; cout << "Child()" << endl; } Child(string s) : Parent(s), mOb1(s + " 1"), mOb2(s + " 2") { ms = s; cout << "Child(string s): " << s << endl; } }; int main() { Child c; // c output: // Object(string s): Default // Parent() // Object(string s): Default 1 // Object(string s): Default 2 // Child() cout << endl; Child cc("cc"); // cc output: // Object(string s): Default // Parent(string s) : cc // Object(string s): cc 1 // Object(string s): cc 2 // Child(string s): cc return 0; }
我们来分析下,类Child c 创建时首先会隐式调用它的父类构造函数 Parent() : Object("Default"),而 Parent 创建时会先调用它的父类 Object 的构造函数 Object("Default")。再来调用成员对象 mOb1 和 mOb2 的构造函数 mOb1("Default 1"), mOb2("Default 2"),最后调用自己的构造函数 Child()。所以最后打印的应该和我们程序中写的是一致的。再来看看对象 cc 的创建过程,因为它是显示调用,所以会调用构造函数 Parent(s) ,而 Parent 的父类 Object 也会调用构造函数 Object(string s) 。额庵后调用成员对象 mOb1 和 mOb2 的构造函数 mOb1(s + "1"), mOb2(s + "2"),最后调用自己的构造函数 Child(string s)。打印的应该也和我们在程序中写的一致。我们来看编译结果
结果和我们分析的是一致的。那么再来看看析构函数的调用顺序,它跟构造函数的顺序刚好相反:1、执行自身的析构函数;2、执行成员变量的析构函数;3、执行父类的析构函数。依旧是在上面的程序基础之上,来看看析构函数的执行顺序。
#include <iostream> #include <string> using namespace std; class Object { string ms; public: Object(string s) { ms = s; cout << "Object(string s): " << ms << endl; } ~Object() { cout << "~Object() : " << ms << endl; } }; class Parent : public Object { string ms; public: Parent() : Object("Default") { ms = "Default"; cout << "Parent()" << endl; } Parent(string s) : Object(s) { ms = s; cout << "Parent(string s): " << s << endl; } ~Parent() { cout << "~Parent() : " << ms << endl; } }; class Child : public Parent { Object mOb1; Object mOb2; string ms; public: Child() : mOb1("Default 1"), mOb2("Default 2") { ms = "Default"; cout << "Child()" << endl; } Child(string s) : Parent(s), mOb1(s + " 1"), mOb2(s + " 2") { ms = s; cout << "Child(string s): " << s << endl; } ~Child() { cout << "~Child() : " << ms << endl; } }; int main() { Child cc("cc"); return 0; }
我们来看看编译结果
通过对继承里的构造与析构函数的学习,总结如下:1、子类对象在创建时需要调用父类构造函数进行初始化;2、先执行父类构造函数然后执行成员的构造函数;3、父类构造函数显示调用需要在初始化列表中进行;4、子类对象在销毁时需要调用父类析构函数进行清理;5、析构顺序与构造顺序对称相反。
欢迎大家一起来学习 C++ 语言,可以加我QQ:243343083。
原文地址:http://blog.51cto.com/12810168/2120704