【C++温故】(2) 类的继承(一)

派生类继承基类中除构造函数和析构函数外的所有成员(is-a)

三种派生方式

包括公有继承、保护继承和私有继承3种,权限逐级降低

默认为私有继承

  • 公有继承
    基类的 public 和 protected 成员在派生类中不变,private 成员派生类不可访问
  • 保护继承
    基类的 public 和 protected 成员在派生类中变为 protected,private 成员派生类不可访问
    (protected 对类外与private一样,不可访问)
  • 私有继承
    基类的 public 和 protected 成员在派生类中变为 private,private 成员派生类不可访问

基类与派生类构造函数的关系

  • 先执行基类构造函数,再执行派生类构造函数;先执行派生类析构函数,再执行基类构造函数
  • 多重继承时按照类声明时的继承顺序依次执行构造函数,与派生类构造函数的参数列表顺序无关
  • 先执行参数列表中的类构造函数,再执行成员初始化;成员初始化顺序按派生类中成员声明的顺序,与构造函数的参数列表顺序无关
#include "stdafx.h"
#include "iostream"
using namespace std;

class A
{
public:
    A(int a)
    {
        keep = a;
        cout << "A constructed: "<<a<<endl;
    }
    ~A()
    {
        cout << "A de-constructed: "<<keep<<endl;
    }
private:
    int keep;
};

class B
{
public:
    B(int b)
    {
        keep = b;
        cout << "B constructed: "<<b<<endl;
    }
    ~B()
    {
        cout << "B de-constructed: "<<keep<<endl;
    }
private:
    int keep;
};

class C:public B,public A
{
public:
    C(int a, int b, int c, int d, int e):A(a),B(b),memberB(d),memberA(c)
    {
        cout << "C constructed: "<<e<<endl;
    }
    ~C()
    {
        cout << "C de-constructed"<<endl;
    }
private:
    A memberA;
    B memberB;
};


int _tmain(int argc, _TCHAR* argv[])
{
    C* objC = new C(1,2,3,4,5);
    delete objC;
    return 0;
}


// 输出为
// B constructed: 2
// A constructed: 1
// A constructed: 3
// B constructed: 4
// C constructed: 5
// C de-constructed
// B de-constructed: 4
// A de-constructed: 3
// A de-constructed: 1
// B de-constructed: 2
  • 先执行 B(b)再执行A(a),是因为继承顺序:class C:public B,public A
  • 先执行memberB(d)再执行memberA(c),是因为声明顺序:private: A memberA; B memberB;
  • 析构函数执行顺序相反

虚继承

对于菱形继承的模式(B1和B2继承自A,C继承B1和B2),普通继承的方式会构造两次A(B1和B2各一次)

#include "stdafx.h"
#include "iostream"
using namespace std;

class A
{
public:
    A()
    {
        cout << "A constructed"<<endl;
    }
    ~A()
    {
        cout << "A de-constructed"<<endl;
    }
public:
    int value;
    void fun(){
        cout<<"func of class A: "<<value<<endl;
    }
};

class B1: public A
{
public:
    B1()
    {
        cout << "B1 constructed"<<endl;
    }
    ~B1()
    {
        cout << "B1 de-constructed"<<endl;
    }
};

class B2: public A
{
public:
    B2()
    {
        cout << "B2 constructed"<<endl;
    }
    ~B2()
    {
        cout << "B2 de-constructed"<<endl;
    }
};

class C:public B1,public B2
{
public:
    C()
    {
        cout << "C constructed"<<endl;
    }
    ~C()
    {
        cout << "C de-constructed"<<endl;
    }
};


int _tmain(int argc, _TCHAR* argv[])
{
    C* objC = new C();
    //objC->value = 1;    //会报错,因为构建了两个A,value和fun不明确
    //objC->fun();
    objC->B1::value = 1;
    objC->B1::fun();
    objC->B2::value = 2;
    objC->B2::fun();
    delete objC;
    return 0;
}

// 输出
// A constructed
// B1 constructed
// A constructed
// B2 constructed
// C constructed
// func of class A: 1
// func of class A: 2
// C de-constructed
// B2 de-constructed
// A de-constructed
// B1 de-constructed
// A de-constructed

为了减少一次 A 的构造,节省内存,可以同时在 B1 和 B2 上虚继承 A

// 上面代码在 B1 和 B2 类声明处做如下修改
class B1:virtual public A
class B2:virtual public A
// main 函数的 objC->B2::fun() 改为
objC->B1::fun();

// 输出
// A constructed
// B1 constructed
// B2 constructed
// C constructed
// func of class A: 1
// func of class A: 2
// C de-constructed
// B2 de-constructed
// B1 de-constructed
// A de-constructed

可以看到,虽然虚继承后表面上还能以 objC->B1:: 的形式调用 fun,但实际上此时 fun 与 objC->B2是同一份拷贝


继承的赋值兼容

#include "stdafx.h"
#include "iostream"
using namespace std;

class A
{
public:
    A()
    {
        cout << "A constructed"<<endl;
    }
public:
    void printA(){cout<<"A::printA()"<<endl;}
};

class B: public A
{
public:
    B()
    {
        cout << "B constructed"<<endl;
    }
public:
    void printB(){cout<<"B::printB()"<<endl;}
};


int _tmain(int argc, _TCHAR* argv[])
{
    B* objB = new B;
    A objB2A = *objB;
    objB2A.printA();
    B objB2A2B = *(B*)(&objB2A);
    objB2A2B.printB();

    delete objB;
    return 0;
}

// 输出
// A constructed
// B constructed
// A::printA()
// B::printB()

如果换为虚继承

// class B:public class A 换成
class B:virtual public class A

则 A -> B 无法转换,即

B objB2A2B = *(B*)(&objB2A);  // 报错

文章《c++ 虚继承与继承的差异》提到
对于普通继承,B 与 A 是一种“is a”的关系,即B类是A类
对于虚继承,B 与 A 是一种“has a”的关系,B类有一个指向A类的虚指针

但一般来说父类转子类是不安全的,需要用 dynamic_cast 做安全检查


  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值