派生类的构造函数和析构函数
简单的派生类的构造函数
在对于派生类进行构造函数定义时,若基类无构造函数,我们则需要将派生类中的所有数据包括从基类当中继承来的数据进行初始化,这就造成了非常大的工作量。
那么如果基类当中拥有构造函数并且在派生类当中继承过来的话我们就可以省许多事
#include <iostream>
using namespace std;
class point
{
protected://便于派生类直接访问避免嵌套调用函数
double x,y;
public:
point(double a=0,double b=0):x(a),y(b){}//参数表构造赋值一体化
};
class circle:protected point//注意保护继承中派生类可访问基类的public与protected
{
protected://便于派生类直接访问避免嵌套调用函数
double r;
public:
circle(double a=0,double b=0,double c=0):point(a,b),r(c){}//参数表构造赋值一体化
};
class cylinder:private circle//注意私有继承派生类可以访问基类的public和protected
{
protected://便于派生类直接访问避免嵌套调用函数
double h;
public:
cylinder(double a=0,double b=0,double c=0,double d=0):circle(a,b,c),h(d){}//参数表构造赋值一体化
friend ostream& operator << (ostream& output,const cylinder& A);//友元运算符重载函数的声明
};
int main()
{
cylinder A(1.2,2.4,3.6,4.8);
cout<<A;
return 0;
}
ostream& operator << (ostream& output,const cylinder& A)
{
//因为protected的使用,在这里对于数据的调用输出就方便了许多
output<<"该圆柱体的高为:"<<A.h<<endl<<"该圆柱体的底面半径为:"<<A.r<<endl<<"该圆柱体底面的一个点的坐标为"<<"("<<A.x<<','<<A.y<<")"<<endl;
return output;
}
实现了构造函数的继承
派生类构造函数一般形式为:
派生类构造函数名(总参数表) :基类构造函数名(参数表)
{在派生类当中新增的数据成员的初始化语句}
在总参数表中包含所有的所需要用到的参数,在总参数表当中包括参数的类型和参数名,而在基类构造函数名后面的参数表中仅仅包括参数名而没有参数类型,因为在这里不是定义基类构造函数而是调用基类构造函数,因此这些参数是实参而不是形参。它们可以是常量、全局变量和派生类构造函数总参数表中的参数。
在建立一个对象的时候,若派生类的构造函数中继承了基类的构造函数,那么派生类构造函数则会优先调用基类的构造函数,然后再执行派生类构造函数本身。同样,先执行派生类的析构函数,再执行基类的析构函数。
有子对象的派生类的构造函数
内嵌对象即为子对象,简单来说就是对象中的对象。即在一个类的定义当中用已经定义过的类作为其数据成员来使用。
#include <iostream>
using namespace std;
class Student
{
protected:
string name;
int num;
public:
Student(string a,int b):name(a),num(b){}
void display(){cout<<name<<' '<<num<<endl;}
};
class Studento:public Student
{
private:
string add;
Student monitor;
public:
Studento(string a,int b,string c,int d,string e):Student(a,b),monitor(c,d){add=e;}
void show_monitor()
{
cout<<"该班长是:";
monitor.display();
}
void show()
{
cout<<"该学生是:";
display();
cout<<"他家住在:"<<add<<endl;
}
};
int main()
{
Studento A("赵铁柱",100,"王大锤",101,"图图幼儿园");
A.show();
A.show_monitor();
return 0;
}
定义派生类构造函数的一般形式为
派生类构造函数名(总参数表):基类构造函数名(参数表),子对象名(参数表)
{派生类当中新增数据成员初始化语句}
执行派生类构造函数的顺序是
1.调用基类构造函数对于基类数据成员初始化
2.调用子对象构造函数,对子对象数据成员初始化
3.再执行派生类构造函数本身,对派生类数据成员进行初始化
多层派生时的构造函数
一个类不仅可以派生出一个派生类,派生类还可以继续派生,就像人类繁衍一样,生生生生生生生生生生生生生生生生生生生生生生生生生生生生生生生生生生生生生生生生生生生生生生生生
#include <iostream>
using namespace std;
class point
{
protected://便于派生类直接访问避免嵌套调用函数
double x,y;
public:
point(double a=0,double b=0):x(a),y(b){}//参数表构造赋值一体化
};
class circle:protected point//注意保护继承中派生类可访问基类的public与protected
{
protected://便于派生类直接访问避免嵌套调用函数
double r;
public:
circle(double a=0,double b=0,double c=0):point(a,b),r(c){}//参数表构造赋值一体化
};
class cylinder:private circle//注意私有继承派生类可以访问基类的public和protected
{
protected://便于派生类直接访问避免嵌套调用函数
double h;
public:
cylinder(double a=0,double b=0,double c=0,double d=0):circle(a,b,c),h(d){}//参数表构造赋值一体化
friend ostream& operator << (ostream& output,const cylinder& A);//友元运算符重载函数的声明
};
int main()
{
cylinder A(1.2,2.4,3.6,4.8);
cout<<A;
return 0;
}
ostream& operator << (ostream& output,const cylinder& A)
{
//因为protected的使用,在这里对于数据的调用输出就方便了许多
output<<"该圆柱体的高为:"<<A.h<<endl<<"该圆柱体的底面半径为:"<<A.r<<endl<<"该圆柱体底面的一个点的坐标为"<<"("<<A.x<<','<<A.y<<")"<<endl;
return output;
}
我们可以发现:
当进行多层派生时我们仅仅需要列出上一层派生类的构造函数即可,在多层构造函数的调用当中,构造函数的调用是从基类逐层往上开始调用。
派生类构造函数的特殊形式
1.不需要对新增的派生类中的成员进行任何操作时,{}中为空即可
2.如果在基类当中没有定义构造函数,或定义了没有参数的构造函数,那么在定义派生类构造函数时可以不写基类构造函数,系统会优先调用基类默认的构造函数。
如果在基类或者子对象类型的声明中都没有定义带参数的构造函数,而且也不许对派生类自己的数据成员初始化,那么可以不必显式的定义派生类构造函数。
如果在基类或者子对象类型的声明中定义了带参数的构造函数,那么就必须显式地定义派生类构造函数,并在派生类构造函数中写出基类或者子对象类型的构造函数以及其参数表
如果在基类中既定义了无参的构造函数又定义了有参的构造函数,则在定义派生类构造函数时,既可以包含基类构造函数及其参数又可以不包含基类构造函数。
派生类的析构函数
在派生时,派生类是不能继承基类的析构函数的,也需要通过派生类的析构函数去调用基类的析构函数。在派生类中可以根据需要定义自己的析构函数,用来对派生类中所增加的成员进行清理工作,基类的清理工作仍由基类的析构函数负责。在执行派生类的析构函数时,系统会自动调用基类的析构函数和子对象的析构函数,对基类的子对象进行清理。
调用的顺序正好与构造函数相反,先调用派生类的析构函数,对于派生类新增的成员进行清理,在调用子对象的析构函数对于子对象进行清理,最后调用基类的析构函数对于基类进行清理。