3-15 继承的构造函数、多重继承、虚继承(虚派生)

继承的构造函数

一个类只继承其直接基类(父类)的构造函数。默认、拷贝、移动构造函数是不能被继承的。

class A{
public:
    A();
};
 
class B : public A{
public:
    using A::A; //遇到这条语句,编译器会把积累的每个构造函数,都会生成一个与之对应的派生类的构造函数。
}

使用 using A::A; 就是让某个名字在当前作用域可见。//遇到这条语句,编译器会把积累的每个构造函数,都会生成一个与之对应的派生类的构造函数(默认、拷贝、移动构造函数除外)。

编译器生成的构造函数大概形式:B(int i, int j, int k):A(i, j, k) {};

如果基类A的构造函数有默认参数的话,那么编译器遇到这种using A::A的时候,就会帮我们在派生类B中构造出多个构造函数来。

1、第一个构造函数是带有所有参数的构造函数;

2、其余的构造函数,每个分别省略掉 一个默认参数(如下例所示);

A(int i, int j, int k=5) {};
 
 
B(int i, int j, int k):A(i, j, k) {};
B(int i, int j):A(i, j) {};

如果基类含有多个构造函数,则多数情况下,派生类会继承所有这些构造函数,但如下情况例外:

1、如果你在派生类中定义的构造函数与基类构造函数有相同的参数列表,那么从基类中继承来的构造函数会被你在派生类中的定义覆盖掉。相当于你只继承了一部分基类中的构造函数。

2、默认、拷贝、移动构造函数不会被继承。

如果类B,只含有using A::A  从类A继承来的构造函数的话,那么编译器就会为B合成构造函数
 

一、继承

继承性是面向对象程序设计的第二大特性,它允许在既有类的基础上创建新类,新类可以继承既有类的数据成员和成员函数,可以添加自己特有的数据成员和成员函数,还可以对既有类中的成员函数重新定义。利用类的继承和派生实现了更高层次的代码可重用性,符合现代软件开发的思想。

C++语言同时支持单一继承和多重继承。单一继承是指派生类只从一个基类继承而来;相应的,多重继承指派生类同时从两个或更多的基类继承而来。

继承的特性:

(1)定义派生类关键字可以是class或者是struct,两者区别是:用class定义派生类,默认的继承方式是private,用struct定义派生类,默认的继承方式为public。新增加的成员默认属性也是class对应private属性,struct对应public属性。

(2)基类不能被派生类继承的两类函数是构造函数和析构函数。

实例:单一继承

#include"stdafx.h"
#include<iostream>
using namespace std;
 
class Other
{
public:
    Other()
    {
        cout<<"constructing Other class"<<endl;
    }
    ~Other()
    {
        cout<<"destructing Other class"<<endl;
    }
};
 
class Base
{
public:
    Base()
    {
        cout<<"constructing Base class"<<endl;
    }
    ~Base()
    {
        cout<<"destructing Base class"<<endl;
    }
};
 
class Derive:public Base
{
private:
    Other ot;
public:
    Derive()
    {
        cout<<"constructing Derive class"<<endl;
    }
    ~Derive()
    {
        cout<<"destructing Derive class"<<endl;
    }
};
 
int main()
{
    Derive d;
 
    return 0;
}

运行结果:

 可以看到定义派生类对象时,构造函数的调用顺序:

      a.先调用基类的构造函数

      b.然后调用派生类对象成员所属类的构造函数(如果有对象成员)

      c.最后调用派生类的构造函数

      析构函数的调用顺序正好与构造函数调用顺序相反。

多重继承

单继承:一个子类继承一个父类;

多重继承:从多个父类产生的子类;

class Grand{
public:
    int m_valuegrand;
 
public:
    Grand(int i):m_valuegrand(i) {};
 
    virtual ~Grand() {};
 
    void myinfo()
    {
        cout << m_valuegrand << endl;
    }
    static int m_static;    //声明成员变量
    //为了能够使用,我们定义这个静态成员变量(分配内存)
};
 
class A: public Grand{
public:
    int m_valuea;
 
public:
    A(int i):Grand(i),m_valuea(i) {};
 
    virtual ~A() {};
 
    void myinfo()
    {
        cout << m_valuea << endl;
    }
};
 
class B{
public:
    int m_valueb;
 
public:
    A(int i):m_valueb(i) {};
 
    virtual ~A() {};
 
    void myinfo()
    {
        cout << m_valueb << endl;
    }
};
 
class C :public A, public B{ //如果是默认继承,则看C是class还是struct,如果是 
                             //class则默认是private,如果是struct,则默认是 
                             //public继承
public:
    C(int i, int j, int k):A(i), B(j), m_valuec(k)
 
    virtual ~C();
    
    void myinfoC(){
        A::myinfo();        //在C中调用A、B的重名成员函数
        B::myinfo();
        cout << m_valuec << endl;
    }
 
public:
    int m_valuec;
 
};
 
int Grand::m_static = 5; //如果你在代码中不用你可以不定义
 
int main(){
    C ctest(1, 3, 5);
    ctest.myinfoC();
    //ctest.myinfo(); //报错,访问不明确
 
    ctest.A::myinfo(); //可以
 
    Grand::m_static = 1;
    A::m_static = 2;
    C::m_static = 3;
   // B::m_static = 5; //不可以没有继承Grand
    ctest.m_stastic = 12; //可以用类对象来引用静态变量
 
}

静态成员变量

派生类的构造函数与析构函数

1、构造一个派生类对象将同时构造并初始化所有基类的子对象。

2、派生类的构造函数初始化列表它只初始化它的直接基类,每个类的构造函数都负责初始化它的直接基类,按照这种传递关系就可以将所有的基类都初始化。

3、派生类构造函数初始化列表将实参分别传递给每个直接基类,积累的构造顺序跟“派生列表”(与初始化列表无关)中积累的出现顺序保持一致。

派生列表:

构造与析构顺序:

概念:显示的初始化基类和隐示的初始化

class Grand{
public:
    int m_valuegrand;
 
public:
    Grand(int i):m_valuegrand(i) {};
 
    virtual ~Grand() {};
 
};
 
class A: public Grand{
public:
    int m_valuea;
 
public:
    A(int i):Grand(i),m_valuea(i) {};
 
    virtual ~A() {};
};
 
class B{
public:
    int m_valueb;
 
public:
    B(): {};
    B(int i):m_valueb(i) {};
 
    virtual ~A() {};
};
 
class C :public A, public B{ 
public:
    C(int i, int j, int k):A(i), m_valuec(k) //这里会默认调用B()
 
    virtual ~C();
 
public:
    int m_valuec;
 
};
 
 
int main(){
    C ctest(1, 3, 5);
 
}

当B中有默认构造函数时,派生类中的构造函数若不包含类B的构造函数那么将默认调用B的默认构造函数

 从多个父类中继承构造函数

class A{
public:
    A(int tv) {};
};
 
class B{
public:
    B(int tv) {};
};
 
class C :public A, public B{
public:
    using A::A;
    using B::B;     
    
    C(int tv): A(tv), B(tv)
 
};

类型转换 

基类指针可以指向一个派生类对象:编译器帮助我们隐示执行这种派生类到基类的转换,转换成功的原因是因为每个派生类对象都包含一个基类对象的部分,所以,基类的引用或指针是可以绑定到基类对象上来的。

Grand *pg = new C(1, 2, 3);
A *pa = new C(1, 2, 3);
B *pb = new C(1, 2, 3);
C myc(1, 2, 4);
Grand mygrand(myc);

虚基类、虚继承

派生列表中,同一个基类只能出现一次,但是有两种情况除外:

1、派生类可以通过它的两个直接基类分别继承同一个间接基类。

这样导致Grand被构造了两次,继承2次Grand是多余的

2、直接继承某个基类,然后通过某个基类间接继承该类

虚基类:无论这个类在继承体系中出现多少次,派生类中都只会包含唯一一个共享的虚基类子内容。

这种虚继承只对C类有意义对于C的父类A和A2没有意义;换句话说,A,A2从Grand类虚继承,只影响到从A,A2这些类中进一步派生出来的类C,而对A,A2没什么影响

每个Grand的子类都要虚继承Grand类,那么才能保证Grand的孙类能够虚继承Grand

class A : virtual public Grand
 
class A2 : virtual public Grand
 
class C : public A , public A2{
public:
    C(int i, int j, int k):A(i), A2(j), Grand(k) {};
};

只要子类中都加virtual继承,那么Grand类自然就成为了“虚基类”;virtual:表达一种意愿:表示后续从我(类A,A2)派生的的类中应该共享虚基类Grand的同一份实例。

说明:

1、现在是类C初始化Grand类,如果以后C类有了孩子,则由C类的孩子初始化Grand类;换句话说:虚基类Grand是由最低成的派生类来初始化。现在C是最低层。

2、初始化顺序是:先初始化虚基类部分,然后再按照派生列表中的出现顺序来初始化其他类。

多个虚基类是如果初始化?按照派生列表中的直接基类来往回追溯,看是否这些基类含有虚基类。销毁和构造顺序正好相反。

总结:
小心使用虚继承,不太提倡使用。

简单,不容易出现二义性,实在必要的情况下才使用多重继承。

能使用单一集成解决问题就不要用多重继承。
 

参考:https://blog.csdn.net/qq_32285693/article/details/89197799

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值