派生类的构造函数和析构函数(C++学习笔记 32)

  引入继承的目的:①派生类继承了基类的成员,实现了原有代码的重用。②实现代码的扩充,只有在派生类中通过添加新的成员,加入新的功能,类的派生才有实际意义。
  基类的构造函数和析构函数不能被继承,在派生类中,如果对派生类新增的成员进行初始化,就需要加入派生类的构造函数,同时,对所有从基类继承下来的成员的初始化工作,还是由基类的构造函数完成

1、派生类构造函数和析构函数的执行顺序

  通常,当创建派生类对象时,首先执行基类的构造函数,随后执行派生类的构造函数;当撤销派生类对象时,则先执行派生类的析构函数,随后再执行基类的析构函数。析构函数的调用顺序与构造函数的调用顺序正好相反。

2、派生类构造函数和析构函数的构造规则

(1)简单的派生类的构造函数

在C++中,派生类构造函数的一般格式为:

派生类名(参数总表):基类名(参数表){
  派生类新增数据成员的初始化语句
}

  • 其中,基类构造函数的参数,通常来源于派生类构造函数的参数总表,也可以用常数值,因为这里是调用基类构造函数,所以这些参数是实参不是形参,它们可以是派生类构造函数总参数表中的参数,也可以是常量和全局变量。
  • 当基类的构造函数没有参数,或没有显式定义构造函数时,派生类可以不向基类传递参数,甚至可以不定义构造函数。
  • 派生类不能继承基类中的构造函数和析构函数。当基类含有带参数的构造函数时,派生类必须定义构造函数,以提供把参数传递给基类构造函数的途径。

例 1:当基类含有带参数的构造函数,派生类构造函数的构造方法。

#include<iostream>
#include<string>
using namespace std;
class Student{	//声明类 Student 
	protected:
		int number;  //学号
		string name;  //姓名
		float score;  //成绩
	public:
		Student(int num1,string name1,float score1){	//	基类构造函数
			number=num1;
			name=name1;
			score=score1; 
		} 
		void print(){
			cout<<"number:"<<number<<endl;
			cout<<"name:"<<name<<endl;
			cout<<"score:"<<score<<endl;
		}
};
class Ustudent:public Student{	//声明公有派生类 Student 
	private:
		string major;
	public:
		Ustudent(int num1,string name1,float score1,string major1):Student(num1,name1,score1){
			major=major1;
		}
		void print1(){
			print();
			cout<<"major:"<<major<<endl;
		}
};
int main(){
	Ustudent stu(12345,"叮叮",100,"数学");
	stu.print1();
	return 0;
}

说明:

① 在类的外部定义派生类的构造函数,在类体内只写构造函数的声明。

如,例 1 可以写成:
Ustudent(int num1,string name1,float score1,string major1);

而在类的外部定义派生类的构造函数:

Ustudent(int num1,string name1,float score1,string major1):Student(num1,name1,score1){
		major=major1;
}

② 若基类使用默认构造函数或不带参数的构造函数,则在派生类中定义构造函数时可略去“:基类构造函数名(参数表)”,此时若派生类不需要构造函数,则可不定义派生类构造函数。
③ 当基类构造函数不带参数时,派生类不一定需要定义构造函数,而当基类的构造函数哪怕只带有一个参数,它所有的派生类都必须定义构造函数,甚至所定义的派生类构造函数的函数体可能为空,仅仅起参数的传递作用。

(2) 派生类的析构函数

  在派生类中可以根据需要定义自己的析构函数,用来对派生类中的所增加的成员进行清理工作,基类的清理工作仍然由基类的析构函数负责。
  在执行派生类的析构函数时,系统会自动调用基类的析构函数,对基类的对象进行清理。析构函数的调用顺序与构造函数正好相反,先执行派生类的析构函数,再执行基类的析构函数。

(3)含有对象成员(子对象)的派生类的构造函数

当派生类中含有内嵌的对象成员(也称子对象时),其构造函数的一般形式为:
派生类名(参数总表):基类名(参数表 0), 对象成员名 1(参数表 1), ···, 对象成员名 n(参数表 n)
{
  派生类新增成员的初始化语句
}

可参考类的组合:https://blog.csdn.net/aaqian1/article/details/84494415

在定义派生类对象时,构造函数的执行顺序如下:
① 调用基类的构造函数,对基类数据成员初始化;
② 调用内嵌对象成员的构造函数,对内嵌对象成员的数据成员初始化;
③ 执行派生类的构造函数体,对派生类数据成员初始化。

  • 撤销对象时,析构函数的调用顺序与构造函数的调用顺序正好相反。
  • 当在派生类中含有多个内嵌对象成员时,调用内嵌对象成员的构造函数顺序由它们在类中声明的顺序确定。

例 2:含有对象成员的派生类构造函数和析构函数的执行顺序

#include<iostream>
using namespace std;
class Base{		//声明基类 Base 
	private:
		int x;
	public:
		Base(int i){ 	//基类的构造函数 
			x=i;
			cout<<"Constructing base class"<<endl;
		}
		~Base(){ 		//基类的析构函数 
			cout<<"Destructing base class"<<endl;
		}
		void show(){
			cout<<"x="<<x<<endl;
		}
};
class Derived:public Base{		//声明公有派生类 Derived
	private:
		Base d;		//d 为基类对象,作为派生类的内嵌对象成员 
	public:
		Derived(int i):Base(i),d(i){	//派生类的构造函数,缀上要调用的基类构造函数和对象成员构造函数 
			cout<<"Constructing derived class"<<endl;
		} 
		~Derived(){
			cout<<"Destructing derived class"<<endl;
		}
};
int main(){
	Derived obj(5);
	obj.show();
	return 0;
}

执行结果:
在这里插入图片描述

例 3:含有多个对象成员的派生类构造函数的执行顺序

#include<iostream>
#include<string>
using namespace std;
class Student{
	protected:
		int number;  //学号
		string name;  //姓名
		float score;  //成绩
	public:
		Student(int num1,string name1,float score1){	//	基类构造函数
			number=num1;
			name=name1;
			score=score1; 
		} 
		void print(){
			cout<<"number:"<<number<<endl;
			cout<<"name:"<<name<<endl;
			cout<<"score:"<<score<<endl;
		}
};
class Ustudent:public Student{		//声明公有派生类 Ustudent 
	private:
		string major;	//专业
		Student auditor1;	//定义对象成员 1(旁听生 ) 
		Student auditor2;	//定义对象成员 2(旁听生 ) 
	public:
		Ustudent(int num1,string name1,float score1,int num2,string name2,
		float score2,int num3,string name3,float score3,string major1):Student(num1,name1,score1),
		auditor2(num2,name2,score2),auditor1(num3,name3,score3){
			major=major1;				
		}
		void print(){
			cout<<"正式生是:"<<endl;
			Student::print();
			cout<<"major:"<<major<<endl<<endl;
		}
		void print_auditor1(){
			cout<<"旁听生是:"<<endl;
			auditor1.print();
		}
		void print_auditor2(){
			auditor2.print();
		}
};
int main(){
	Ustudent stu(1,"小明",92,2,"小红",99,3,"小兰",63,"英语");
	stu.print();
	stu.print_auditor1();
	stu.print_auditor2();
	return 0;
}

执行结果:
在这里插入图片描述
  调用内嵌对象成员构造函数的顺序由它们在类中声明的顺序确定。 在本例中有两个内嵌对象成员,虽然在派生类构造函数首部,内嵌对象成员 auditor2 的构造函数写在 auditor1 的前面,但是调用顺序还是先执行 auditor1 的构造函数。
  如果派生类的基类也是一个派生类,每个派生类只需负责其直接基类数据成员的初始化,依次上溯。

猜你喜欢

转载自blog.csdn.net/aaqian1/article/details/84915540