继承过程中的构造函数
有参/无参构造函数
需要注意,继承操作不能继承基类的构造函数。然而,派生类中包含了基类中那些被继承来的数据成员,这些成员也是需要初始化的。因此,牵扯到在派生类中,对基类的成员变量进行初始化的问题时,往往需要在派生类的构造函数中,采用类似初始化列表的方式,进行初始化。
class dad{ //基类
private:
int fame,money;
public:
dad(){ //无参构造函数
fame=0,money=0;
}
dad(int F,int M){ //有参构造函数
fame=F,money=M;
}
void output(){ //基类的公有成员函数
cout<<fame<<" "<<money<<" ";
}
};
class son: public dad //公有继承
{
private:
int gf;
public:
son():dad() //基类无参构造函数+初始化列表
{
gf=0;
}
son(int n):dad(n,10) //基类有参构造函数+初始化列表
{
gf=n;
}
void print(){
output();//直接访问基类公有成员函数
cout<<gf<<endl;
}
};
int main()
{
son a;
son b(6);
a.print();
b.print();
return 0;
}
输出:
0 0 0
9 10 6
在基类构造函数无法被继承的条件下,初始化列表实现了同时初始化基类变量和派生类新增变量的效果。
析构函数
析构函数属于构造函数,不能被继承。为了明确析构顺序,基类的析构函数前需要加virtual
class dad{
public:
virtual ~dad(){cout<<"666";}
};
class son: public dad
{
···
};
子对象与继承
子对象相关知识,可以参考“子对象与堆对象”那一节
二者在初始化的时候都会用到初始化列表,但继承使用类名(参数)
,而子对象使用对象名(参数)
class dad{
private:
int d;
public:
dad(int D){ //基类的有参构造函数
d=D;
}
};
class son: public dad
{
private:
dad dd;//子对象dd
int s;
public:
son(int d1,int d2):dad(d1),dd(d2)
{
s=0;
}
};
构造函数调用顺序问题
构造函数:基类->子对象->派生类;
析构函数:派生类->子对象->基类;
子类型
当一个类son中包含了另一个类dad中的所有行为,则称类son为类dad的子类型。例如,在公有继承下,派生类是基类的子类型。满足如下赋值规则:
- son类的对象可以给dad类的对象赋值
- son类的对象可以给dad类的对象引用赋值
- son类的对象地址可以给dad类的对象指针赋值
class son: public dad{···} //公有继承
son s;
dad d1=s;
dad &d2=s;
dad *d3=&s;
多重继承中的构造函数调用
多重继承即一个派生类脱胎于多个基类。使用该派生类创建对象时,先执行基类的构造函数,再执行派生类自己的构造函数。基类构造函数的执行顺序取决于定义派生类时规定的顺序。基类数据成员的初始化仍要借助初始化列表,但初始化列表中的顺序不会影响基类构造函数的调用顺序。例如:
class dad1{···};
class dad2{···};
class dad3{···};
class son:public dad1, public dad2, public dad3 //在此规定调用顺序
{
son():dad2(),dad1(),dad3(){···}//派生类构造函数
};
在上面的例子中,使用son定义对象时,按照dad1->dad2->dad3->son
的顺序,调用构造函数。
ps.析构函数的调用顺序与构造函数相反
多重继承的二义性
注意,只有存在多重继承时,才会有所谓的“二义性”。
- 一个派生类有多个基类,这些基类中有同名函数。需要加
类名::函数名
表示调用某个基类的相应函数
class dad1{ //基类1
public:
void print(){
cout<<666;
}
};
class dad2{ //基类2
public:
void print(){
cout<<"999";
}
};
class son: public dad1, public dad2
{
public:
void output1(){
dad1::print(); //调用基类1的print
}
};
- 一个派生类有多个基类,这些基类又有一个共同的基类,该“祖先”基类中有一个函数。如果在派生类中直接调用,会存在不知道通过哪个爸爸联系爷爷的争议,也需要
类名::函数名
加以限制
class grandpa{ //“基类的共同基类”
public:
void print(){
cout<<"666";
}
};
class dad1: public grandpa {};
class dad2: public grandpa {};
class son: public dad1, public dad2
{
public:
void output1(){
dad1::print();//通过dad1调用
}
};