C++笔记(二)

本文详细介绍了C++中的四种继承类型:公有、私有和保护继承,以及虚继承,探讨了它们的特点和使用场景。此外,还讨论了构造函数和析构函数的执行顺序以及多重继承可能导致的二义性问题。通过实例展示了如何解决这些问题,如使用虚继承避免重复成员。最后,提到了虚继承在菱形继承中的应用,以确保单一继承路径。
摘要由CSDN通过智能技术生成

1、三种继承关系

  • 公有继承的特点是基类的公有成员和保护成员作为派生类的成员时,它们都保持原有的状态,而基类的私有成员仍然是私有的,不能被这个派生类的子类所访问
  • 私有继承的特点是基类的公有成员和保护成员都作为派生类的私有成员,并且不能被这个派生类的子类所访问。(最常见)
  • 保护继承的特点是基类的所有公有成员和保护成员都成为派生类的保护成员,并且只能被它的派生类成员函数或友元访问,基类的私有成员仍然是私有的。(子类对象不可能直接访问到基类的成员)
  class Derived : protected Base
  {
       public:
       int getx(void){return _x;}  //_x是基类的成员数据(公有的/保护的)

    };
   int main(int argc,char **argv)
   {

      Base apple;
      Derived banana;   
      cout << banana.Base::x << endl; // 错误的,
      cout<<  banana.getx()<<endl;    //正确的,派生类成员函数
   }
   

在这里插入图片描述
2.类继承关系中构造函数、析构函数执行次序

  • 先是构造基类,再构造子类
  • 显示析构子类,再析构基类
#include <iostream>
using namespace std;
 class Base // 基类、父类
{
public:
     Base(){cout <<  "B" << endl;}
    ~Base(){cout << "~B" << endl;}
};

 // 派生类、子类
class Derived : public Base   // is-a关系
{
public:
    Derived() {cout <<  "D" << endl;}
    ~Derived(){cout << "~D" << endl;}
};

int main(void)
{
    Derived d; // 先构造Base,再构造Derived;
               // 先析构Derived,再析构Base
}

执行结果:

gec@ubuntu:~$ ./main
B
D
~D
~B

3,多重继承、多重is-a关系

  • 多重继承要注意二义性
  • 父类的作用域比子类的作用域大,因此在子类和父类定义一个相同的变量时,子类的变量会掩盖父类的变量,父类的变量还在
  • 当子类继承两个父类,而且两个父类有相同的变量时,这时我们就不可以直接通过父类来调用该变量,而是应该调用基类作用域中的变量
#include <iostream>

using namespace std;

class A
{
public:
    void funA(){cout<< "class A" << endl;}
    int x;
    int y=10;
};

class B
{
public:


    void funB(){cout<< "class B" << endl;}
    int x;
};

// 同时继承两个父类
class C : public A, public B
{

};

int main()
{
    C c;
    c.funA();
    c.funB();
    c.y = 100;  //正确
    c.x =100;  //错误的
    c.A::x = 100;

    return 0;
}

4.虚继承、菱形继承

  • 如果都是实继承,那么A类中的x变量,在D类中就有两个xx,而且是相同的。但是这样是不符合我们想要的(但却是合法的)。
  • 因此B,和C类要虚继承于A
  • 让D类直接来调用A类

在这里插入图片描述
虚继承:

#include <iostream>

using namespace std;

// 交通工具
class A
{
public:
    A(int id):ID(id){cout << "构造A" << endl;}
    int ID;
};

class B : virtual public A
{
public:
    // 以下语句中的A(id)是显式地调用A的构造函数
    // 当直接定义B对象的时候,显然会调用它
    // 但是如果是通过D触发,那么就不会调用
    B(int id):A(id){cout << "构造B" << endl;}
};

class C : virtual public A
{
public:
    C(int id):A(id){cout << "构造C" << endl;}
};

class D : public B, public C
{
//初始化列表不但要初始化B和C,还要初始化A类
public:
    D(int id):A(id),B(id),C(id){cout << "构造D" << endl;}
};


int main()
{
    D d(99);
    cout << d.ID << endl;
    return 0;
}

执行结果:

构造A
构造B
构造C
构造D
99

实继承:

#include <iostream>

using namespace std;

// 交通工具
class A
{
public:
    A(int id=0):ID(id){cout << "构造A" << endl;}
    int ID;
};

class B :  public A
{
public:
    // 以下语句中的A(id)是显式地调用A的构造函数
    // 当直接定义B对象的时候,显然会调用它
    // 但是如果是通过D触发,那么就不会调用
    B(int id=0):A(id){cout << "构造B" << endl;}
};

class C :  public A
{
public:
    C(int id=0):A(id){cout << "构造C" << endl;}
};

class D : public B, public C
{
//初始化B,C类即可
public:
    D(int id):B(id),C(id){cout << "构造D" << endl;}
};


int main()
{
    D d(99);
    cout << d.B::ID << endl;
    return 0;
}

执行结果:

构造A
构造B
构造A
构造C
构造D
99


构造了两次A,不是我们想要的

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值