继承过程中的构造函数

继承过程中的构造函数

有参/无参构造函数

需要注意,继承操作不能继承基类的构造函数。然而,派生类中包含了基类中那些被继承来的数据成员,这些成员也是需要初始化的。因此,牵扯到在派生类中,对基类的成员变量进行初始化的问题时,往往需要在派生类的构造函数中,采用类似初始化列表的方式,进行初始化。

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调用
    }
};

猜你喜欢

转载自blog.csdn.net/pyx2466079565/article/details/107142657