派生类的构造函数和析构函数
目录
1、简单的派生类的构造函数
2、有子对象的派生类的构造函数
3、多层派生时的构造函数
4、派生类构造函数的特殊形式
5、派生类的析构函数
-----------------------------------------------------------------------------------------------------------
继承与派生的目的
继承的目的:实现代码重用。
派生的目的:当新的问题出现,原有程序无法解决(或不能完全解决)时,需要对原有程序进行改造。
派生类的构造函数
A、基类的构造函数不被继承,派生类中需要声明自己的构造函数。
B、声明构造函数时,只需要对本类中新增成员进行初始化,
调用基类构造函数对继承来的基类成员初始化。
C、派生类的构造函数需要给基类的构造函数传递参数。
1、简单的派生类的构造函数
简单派生类只有一个基类,而且只有一级派生,在派生类的数据成员中不包含基类的对象(即子对象)。
在定义派生类的构造函数时除了对自己的数据成员进行初始化外,还必须调用基类的构造函数初始化基类的数据成员。
单一继承时的构造函数格式如下:
派生类名::派生类名(基类所需的形参,本类成员所需的形参):基类名(基类参数表)
{
本类成员初始化赋值语句;
}
派生类名后的参数表分别列出基类和派生类构造函数的形参(有类型和形参变量)。
基类参数表列出传递给基类构造函数的实参,是派生类构造函数总参数表中的参数。
用派生类构造函数的形参做基类构造函数的实参。
2、有子对象的派生类的构造函数
类的数据成员除了是标准类型或系统提供的类型如string外,还可以是类类型,如声明一个类时包含类型的数据成员:
Student S1;
Student是已声明过的类名,S1是该类的对象。我们称S1为子对象
派生类构造函数的任务包括:
A、对基类数据成员初始化
B、对子对象的数据成员初始化
C、对派生类的数据成员初始化
派生类构造函数一般形式:
派生类名::派生类名(总参数表):基类名(实参表),子对象名(参数表)
{
派生类新增成员的初始化语句;
}
执行派生类构造函数的顺序是:
A、调用基类构造函数,初始化基类数据成员
B、 调用子对象构造函数,初始化子对象数据成员
C、 执行派生类构造函数,初始化派生类数据成员
备注:编译系统在此根据参数名(而不是参数的顺序)决定各参数表中参数之间的传递关系。如有多个子对象,要逐个列出子对象及其参数表。
案例:
class Student //声明基类
{
public: //公有成员
Student(int n, string na) <span style="white-space:pre"> </span> //基类构造函数
{
num = n;
name = na;
}
void display()
{
cout << "Num:" << num << endl;
cout << "Name:" << name << endl;
}
protected: //保护成员
int num;
string name;
};
class Student1 :public Student
{
public:
Student1(int n, string nam, int n1, string nam1, int a, string ad) :
Student(n, nam), monitor(n1, nam1) //派生类,基类,子对象
{
age = a;
addr = ad;
}
void show()
{
cout << "this student is:" << endl;
display();
cout << "Age:" << age << endl;
cout << "Address:" << addr << endl << endl;
}
void show_monitor()
{
cout << endl << "Class Monitor is:" << endl;
monitor.display(); //调用对象初始化值
}
private:
Student monitor;
int age;
string addr;
};
int _tmain(int argc, _TCHAR* argv[])
{
Student1 stu(1001, "八戒", 10000, "二师兄", 20, "高老庄");
stu.show();
stu.show_monitor();
return 0;
}
3、多层派生时的构造函数(爷->父->孙)
一个类可以出一个派生类,派生类还可以继续派生,形成派生的层次结构。多层派生时怎么写派生类的构造函数?
如果派生类的基类也是派生类,那么每个派生类只需要负责其直接的基类数据成员的初始化
基类的构造函数首部:
Student(int n,string nam);
派生类Student1的构造函数首部:
Student1(int n,string nam,int a):Student(n,nam);
派生类Student2的构造函数首部:
Student2(int n,string nam,int a,int s):Student1(n,nam,a);
初始化顺序是:
A、先初始化基类Student的数据成员num,nam;
B、再初始化直接派生类Student1的数据成员age;
C、最后初始化间接派生类Student2的数据成员score;
从结果中很清楚的说明了其构造函数和析构函数的调用顺序。
4、派生类构造函数的特殊形式
A、当不需要对派生类新增成员进行初始时,派生类构造函数的函数体可以为空。
B、如果在基类里没有定义构造函数,或定义了没有参数的构造函数,在定义派生类构造函 数时可以不写基类构造函 数。
C、如果在基类和子对象的类中都没有定义带参数的构造函数,也不需要对派生类自己的数 据成员进行初始化,可以 不定义派生类构造函数。
D、如果在基类或子对象的类声明里定义了带参数的构造函数,就必须定义派生类构造函 数,并在派生类构造函数 中写出基类或子对象类的构造函数及其参数表。
E、如果在基类既定义了无参数的构造函数也定义了有参数的构造函数,在定义派生类构造 函数时,既可以包含基类 构造函数及其参数,也可以不包含基类构造函数。
5、派生类的析构函数
派生类不能继承基类的析构函数,注销派生类对象时,需要派生类的析构函数去调用基类的析构函数。
首先、执行派生类自己的析构函数,清理派生类新增加的成员,
其次、再调用子对象类的析构函数清理子对象,
最后、调用基类析构函数清理基类的成员。
执行顺序:派生类->子对象->基类
#include "stdafx.h"
#include <iostream>
#include <string>
class C
{
private:
std::string c;
public:
C(){};
C(std::string c);
~C();
void showC();
};
C::C(std::string c)
{
this->c = c;
std::cout << "构造c值为" << c << "的对象c" << std::endl;
}
C::~C()
{
std::cout << "清理c值为" << c << "的对象c" << std::endl;
}
void C::showC()
{
std::cout << "c=" << this->c << std::endl;
}
class D :private C
{
private:
std::string d;
C c;
public:
D(std::string d, std::string c1, std::string c2);
~D();
void showD();
};
D::D(std::string d, std::string c1, std::string c2) :c(c2), C(c1) //参数传递给基类,也可以不写C(c1)或c(c2),那么就调<span style="white-space:pre"> </span> //用了C类中无参构造函数
{
this->d = d;
std::cout << "构造对象d" << std::endl;
}
D::~D()
{
std::cout << "清理对象d" << std::endl;
}
void D::showD()
{
showC();
c.showC();
std::cout << "d=" << this->d << std::endl;
}
int main()
{
{
D d("ddd", "ccc1", "ccc2");
d.showD();
}
return 0;
}