1、思考
如何初始化父类成员?父类构造函数和子类构造函数有什么关系?
2、子类对象的构造
- 子类中可以定义构造函数
- 子类构造函数
— 必须对继承而来的成员进行初始化
1、直接通过初始化列表或者赋值的方式进行初始化
2、调用父类构造函数进行初始化 - 父类构造函数在子类中的调用方式
— 默认调用
1、适用于无参构造函数和使用默认参数的构造函数
— 显示调用
2、通过初始化列表进行调用(适用于所有父类构造函数) - 父类构造函数的调用
class Child :public Parent
{
public:
Child() //隐式调用了父类的无参构造函数
{
cout << "Child()" << endl;
}
Child(string s) :Parent("Parameter to Parent") //显示调用父类的构造函数
{
cout << "Child():" << s << endl;
}
};
程序1:
#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)
{
cout << "Child((string s): " << s << endl;
}
};
int main()
{
Child c; //error,no Parent construtot to call
return 0;
}
解析:Child c 这个对象的构造肯定是调用无参构造函数,所以先调用父类的无参构造函数,再调用子类的无参构造函数。程序出错的原因在于没有定义父类的无参构造函数,加上即可。
Parent()
{
cout << "Parent()" << endl;
}
程序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)
{
cout << "Child((string s): " << s << endl;
}
};
int main()
{
//Child c;
Child cc("cc");
return 0;
}
解析:第33行代码Child cc("cc");
对应的是 Child类的带参数的构造函数的构造,但是因为没有显式调用父类的构造函数,所以默认就是调用无参构造函数,所以结果如图。
程序3:如何显式调用父类带参数的构造函数的写法
#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)
{
cout << "Child((string s): " << s << endl;
}
};
int main()
{
Child c;
Child cc("cc");
return 0;
}
如何显式调用父类带参数的构造函数:在子类的带参数的构造函数搞一个初始化列表即可。
- 构造规则
— 子类对象在创建时会首先调用父类的构造函数
— 先执行父类构造函数再执行子类的构造函数
— 父类构造函数可以被隐式调用或者显式调用 - 对象创建时构造函数的调用顺序
1、 调用父类的构造函数
2、 调用成员变量的构造函数
3、 调用类自身的构造函数
口诀心法:先父母,后客人,再自己
程序3:
#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")
{
cout << "Parent()" << endl;
}
Parent(string s):Object(s)
{
cout << "Parent(string s): " << s << endl;
}
};
class Child : public Parent
{
Object mO1;
Object mO2;
public:
Child():mO1("Default 1"), mO2("Default 2")
{
cout << "Child()" << endl;
}
Child(string s):Parent(s), mO1(s + " 1"), mO2(s + " 2")
{
cout << "Child((string s): " << s << endl;
}
};
int main()
{
Child c;
cout << endl;
Child cc("cc");
return 0;
}
解析:这个程序既有继承,又有组合。Child c;
先调用父类Parent 的默认构造函数,但是它显式调用了Parent父类Object的构造函数,所以先调用Object的构造函数,再调用Parent 的默认构造函数。再调用成员变量的构造函数,已经显式调用了。最后调用自身的构造函数。
这里要学的就是成员变量要怎么在初始化列表写参数,是对象名,不是类名。
3、子类对象的析构
- 析构函数的调用顺序与构造函数相反
1、调用自身的析构函数
2、执行成员变量的析构函数
3、执行父类的析构函数
#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 c;
cout << endl;
Child cc("cc");
cout << endl;
return 0;
}
小结:
- 子类对象在创建时需要调用父类构造函数进行初始化
- 先执行父类构造函数然后执行成员的构造函数
- 父类构造函数显示调用需要在初始化列表中进行
- 子类对象在销毁时需要调用父类析构函数进行清理
- 析构顺序与构造按顺序对称相反。