注:博客中内容主要来自《狄泰软件学院》,博客仅当私人笔记使用。
测试环境:Ubuntu 10.10
GCC版本:9.2.0
一、思考
如何初始化父类成员?父类构造函数和子类构造函数有什么关系?
二、子类对象的构造
1)子类中可以定义构造函数
2)子类构造函数
- 必须对继承而来的成员进行初始化
*直接通过初始化列表或者赋值的方式进行初始
*调用父类构造函数进行初始化
3)父类构造函数在子类中的调用方式
- 默认调用
*适用于无参构造函数和使用默认参数的构造函数
- 显示调用
*通过初始化列表进行调用(调用方法)
*适用于所有父类构造函数(可调用的构造函数种类——全部)
4)父类构造函数的调用
class Child : public Parent
{
public:
Child() /*隐式调用*/ //隐式调用父类构造函数
{
cout << "Child()" <<endl;
}
Child(string s) /*显示调用*/
: Parent("Parameter to Parent") //显示调用父类构造函数
{
cout << "Child() : " << s << endl;
}
};
编程实验
子类的构造初探
46-1.cpp
#include <iostream>
#include <string>
using namespace std;
class Parent
{
public:
Parent()
{
cout << "Parent()" << endl;
}
Parent(string s)
{
cout << "Parent(string s) : " << s << endl;
}
};
class Child : public Parent
{
public:
Child()
{
cout << "Child()" << endl;
}
//Child(string s) //修改前
Child(string s) : Parent(s) //修改后:添加初始化列表调用父类构造函数
{
cout << "Child(string s) : " << s << endl;
}
};
int main()
{
Child c;
Child cc("cc");
return 0;
}
操作:
1) g++ 46-1.cpp -o 46-1.out编译正确,打印结果:
//子类c创建
Parent() //定义时没有参数,默认调用父类子类无参构造函数
Child()
//子类cc创建
Parent() //因为子类Child没有显示调用父类构造函数,就默认调用父类无参构造函数
Child(string s) : cc
分析:
子类Child实例化时,先调用父类构造函数,在调用自身构造函数。
2) 修改代码,将子类含参构造函数加上初始化列表:
#include <iostream>
#include <string>
using namespace std;
class Parent
{
public:
Parent()
{
cout << "Parent()" << endl;
}
Parent(string s)
{
cout << "Parent(string s) : " << s << endl;
}
};
class Child : public Parent
{
public:
Child()
{
cout << "Child()" << endl;
}
Child(string s) : Parent(s) //Parent(s)来自参数string s
{
cout << "Child(string s) : " << s << endl;
}
};
int main()
{
Child c;
Child cc("cc");
return 0;
}
g++ 46-1.cpp -o 46-1.out编译正确,打印结果:
//子类c创建
Parent() //定义时没有参数,默认调用父类子类无参构造函数
Child()
//子类cc创建
Parent(string s) : cc //因为子类Child显示调用父类构造函数
Child(string s) : cc
分析:
对于初始化列表中的父类构造函数先被调用,执行完毕后在运行子类构造函数。
5)构造规则
- 子类对象在创建时会首先调用父类的构造函数
- 先执行父类构造函数再执行子类的构造函数
- 父类构造函数可以被隐式调用或者显示调用
6)对象创建时构造函数的调用顺序
1、调用父类的构造函数
2、调用成员变量的构造函数 ??
3、调用类自身的构造函数
口诀心法:
先父母,后客人,再自己。
编程实验——分析先父母,后客人,再自己
子类构造深度解析
46-2.cpp
#include <iostream>
#include <string>
using namespace std;
class Object
{
public:
Object(string s)
{
cout << "Object(string s) : " << s << endl;
}
};
class Parent : public Object
{
public:
Parent() : Object("Default") //Object没有无参构造函数,这里必须显示调用有参构造函数
{
cout << "Parent()" << endl;
}
Parent(string s) : Object(s)
{
cout << "Parent(string s) : " << s << endl;
}
};
class Child : public Parent //继承关系 先父母
{
Object mO1; //组合关系 后客人
Object mO2;
public: //成员列表初始化Object(体现后客人)
Child() : mO1("Default 1"), mO2("Default 2") //如果这里不显示调用Object构造函数,报错。因为:Object没有匹配的无参构造函数,这里必须显示调用
{
cout << "Child()" << endl;
}
//定义s后,就能在初始化列表使用!
Child(string s) : Parent(s), mO1(s + " 1"), mO2(s + " 2") //通过成员列表初始化Object(后客人)(因为没有无参构造函数)
{
cout << "Child(string s) : " << s << endl;
}
};
int main()
{
Child cc("cc");
return 0;
}
操作:
1) g++ 46-2.cpp -o 46-2.out编译正常,打印结果:
Object(string s) : cc
Parent(string s) : cc
Object(string s) : cc 1
Object(string s) : cc 2
Child(string s) : cc
分析:
继承关系:Child继承Parent,Parent继承Object,Child组合Object。打印内容:
先父母:Parent类:Object(string s) : cc,在自己Parent(string s) : cc
再客人:Object类:Object(string s) : cc 1, Object(string s) : cc 2
后自己:Child类:Child(string s) : cc
2) 修改Child类的成员对象类型为Parent,代码如下:
#include <iostream>
#include <string>
using namespace std;
class Object
{
public:
Object(string s)
{
cout << "Object(string s) : " << s << endl;
}
};
class Parent : public Object
{
public:
Parent() : Object("Default") //Object没有无参构造函数,这里必须显示调用有参构造函数
{
cout << "Parent()" << endl;
}
Parent(string s) : Object(s)
{
cout << "Parent(string s) : " << s << endl;
}
};
class Child : public Parent //继承关系 先父母
{
Parent mO1;
Parent mO2;
public: //成员列表初始化Object(体现后客人)
Child() : mO1("Default 1"), mO2("Default 2") //如果这里不显示调用Object构造函数,报错。因为:Object没有匹配的无参构造函数,这里必须显示调用
{
cout << "Child()" << endl;
}
//定义s后,就能在初始化列表使用!
Child(string s) : Parent(s), mO1(s + " 1"), mO2(s + " 2") //通过成员列表初始化Object(后客人)(因为没有无参构造函数)
{
cout << "Child(string s) : " << s << endl;
}
};
int main()
{
Child cc("cc");
return 0;
}
g++ 46-2.cpp -o 46-2.out编译正确,打印结果:
Object(string s) : cc
Parent(string s) : cc
Object(string s) : cc1
Parent(string s) : cc1
Object(string s) : cc2
Parent(string s) : cc2
Child(string s) : cc
三、子类对象的析构(顺序和上边相反)
1)析构函数的调用顺序与构造函数相反
1、执行自身的析构函数
2、执行成员变量的析构函数
3、执行父类的析构函数
编程实验
对象的析构
46-3.cpp
#include <iostream>
#include <string>
using namespace std;
class Object
{
string ms;
public:
Object(string s)
{
cout << "Object(string s) : " << s << endl;
ms = s;
}
~Object()
{
cout << "~Object() : " << ms << endl;
}
};
class Parent : public Object
{
string ms;
public:
Parent() : Object("Default")
{
cout << "Parent()" << endl;
ms = "Default";
}
Parent(string s) : Object(s)
{
cout << "Parent(string s) : " << s << endl;
ms = s;
}
~Parent()
{
cout << "~Parent() : " << ms << endl;
}
};
class Child : public Parent
{
Object mO1;
Object mO2;
string ms;
public:
Child() : mO1("Default 1"), mO2("Default 2") //通过初始化列表,显示调用
{
cout << "Child()" << endl;
ms = "Default";
} //初始化列表
Child(string s) : Parent(s), mO1(s + " 1"), mO2(s + " 2") //显示调用
{
cout << "Child(string s) : " << s << endl;
ms = s;
}
~Child()
{
cout << "~Child() : " << ms << endl;
}
};
int main()
{
Child cc("cc");
cout << endl;
return 0;
}
操作:
1) g++ 46-3.cpp -o 46-3.out编译正确,打印结果:
Object(string s) : cc //父类
Parent(string s) : cc
Object(string s) : cc 1 //客人
Object(string s) : cc 2
Child(string s) : cc //自己
~Child() : cc //自己
~Object() : cc 2 //客人
~Object() : cc 1
~Parent() : cc //父类
~Object() : cc
分析:
析构函数调用顺序和构造顺序相反:先自己->在客人->后父母。
小结
1)子类对象在创建时需要调用父类构造函数进行初始化
2)先执行父类构造函数然后执行成员的构造函数
3)父类构造函数显示调用需要在初始化列表中进行
4)子类对象在销毁时需要调用父类析构函数进行清理
5)析构顺序与构造顺序对称相反