施磊老师c++(六)

继承与多态-多重继承

1.虚基类和虚继承

本节内容

  1. 多重继承?
    代码复用, 一个派生类 有多个基类
    抽象类—有纯虚函数的类

  2. 虚基类
    virtual的两种修饰

  3. 修饰成员方法----叫做虚函数

  4. 修饰继承方式—>虚继承. 被虚继承的类, 称为虚基类

class A { private: int ma; };
class B : virtual public A
{
private:
    int mb;
};
  1. 看一下虚基类的结构
    基类被虚继承后的 内存布局 如下: 多了 vbptr 和 vbtable, 注意区别 vfptr与vftable
    virtual base 与 virtual func

    class A size(4):
            +---
     0      | ma
            +---
    
    
    
    
    class B size(12):  ----  x86上
            +---
     0      | {vbptr}      -----  重点 :  指向vbtable
     4      | mb
            +---
            +--- (virtual base A)   ----  这里开始是A
     8      | ma
            +---
    
    B::$vbtable@:
     0      | 0
     1      | 8 (Bd(B+0)A)      ----- 重点, 相较于vbptr的偏移量
    
    
    
  2. 根据这个结构, 思考下面这个例子为什么出错?

    #include <iostream>
    using namespace std;
    
    
    class A 
    { 
    private: 
        int ma;
    public:
        virtual void func()
        {
            cout << "call A:func" << endl;
          }
    
        //添加一个 new重载, 看看 new和delete的一不一样
        void operator delete(void* ptr)
        {
            cout << "delete p: " << ptr << endl;
            free(ptr);
    
        }
    };
    class B : virtual public A
    {
    private:
        int mb;
    public:
        void func()
        {
            cout << "call B:func" << endl;
        }
        //添加一个 new重载, 看看 new和delete的一不一样
        void* operator new(size_t size)
        {
            void* p = malloc(size);
            cout << "new p: " << p << endl;
            return p;
        }
    };
    
    int main() { 
    
        // 基类指针指向派生类对象, 永远指向 派生类内存起始地址
        // 正式由于这个原因, 使得虚基类中, 派生类继承的虚基类, 在内存结构最下面, 起始是vbptr,  使得堆上释放会出错 
        A *a= new B();
        a->func();  // 可以正确调用
        delete a;   // 但是释放有问题
          
        return 0;
    }
    
    
    /*
    new p: 00BB6FA0
    call B:func
    delete p: 00BB6FA8   
    */
    
    //发现new和delete的不是一块地方, --- 编译器不同, 可能会没有这个问题
    //vs不行, 但是linux的g++确是正确的
    
    //实测发现 g++ (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0 的g++ 貌似也不行,没搞明白
       
    

    vs里不使用堆, 就没有这个问题

    B b;
    A *a= &b;
    a->func();  
    

2.菱形继承—怎么解决?

本节内容

  1. 菱形继承示意图

            A
           / \
          /   \
         B     C
          \   /
           \ /
            D
              A
             /|\
            / | \
           B  C  D
            \ | /
             \|/
              E
            A
           / \
          B   C
           \ / \
            D   E
             \ /
              F
    
  2. 菱形继承面临的问题
    第一个图中,在菱形继承中,D 会包含两份 A 的成员(通过 BC),这可能导致二义性和资源浪费。

  3. cpp开源代码,很少有 多重继承

  4. 实例: 重复构造, 浪费资源

    #include <iostream>
    using namespace std;
    
    
    class A 
    { 
    private: 
        int ma;
    public:
        A(int data) :ma(data) { cout << "A()" << endl; }
    
        ~A() { cout << "~A()" << endl; }
        
    };
    
    
    class B : public A
    {
    private:
        int mb;
    public:
        B(int data) :A(data), mb(data) { cout << "B()" << endl; }
    
        ~B() { cout << "~B()" << endl; }
    
    };
    
    class C : public A
    {
    private:
        int mc;
    public:
        C(int data) :A(data), mc(data) { cout << "C()" << endl; }
    
        ~C() { cout << "~C()" << endl; }
    
    };
    
    class D : public B, public C
    {
    private:
        int md;
    public:
        D(int data) :B(data),C(data), md(data) { cout << "D()" << endl; }
    
        ~D() { cout << "~D()" << endl; }
    
    };
    
    int main() { 
    
        D d(10);
          
        return 0;
    }
    
    
    
    A()
    B()
    A()
    C()
    D()
    ~D()
    ~C()
    ~A()
    ~B()
    ~A()
    

    使用虚继承解决, 继承的A全部换为虚继承

  5. 特别注意:由于B,C都是虚继承, D继承B,C时, A的vbtable是在D里面的, 因此D里面必须对A构造, 默认构造是不需要这样的, 但是A里面写了 自定义带参构造, 就需要写上了

    #include <iostream>
    using namespace std;
    
    
    class A 
    { 
    private: 
        int ma;
    public:
        A(int data) :ma(data) { cout << "A()" << endl; }
    
        ~A() { cout << "~A()" << endl; }
        
    };
    
    
    class B : virtual public A
    {
    private:
        int mb;
    public:
        B(int data) :A(data), mb(data) { cout << "B()" << endl; }
    
        ~B() { cout << "~B()" << endl; }
    
    };
    
    class C : virtual public A
    {
    private:
        int mc;
    public:
        C(int data) :A(data), mc(data) { cout << "C()" << endl; }
    
        ~C() { cout << "~C()" << endl; }
    
    };
    
    class D : public B, public C  //B和C里面的vbptr存储的偏移量 是不同的
    {
    private:
        int md;
    public:
        D(int data) :A(data), B(data),C(data), md(data) { cout << "D()" << endl; }
    
        ~D() { cout << "~D()" << endl; }
    
    };
    
    int main() { 
    
        D d(10);
          
        return 0;
    }
    
    
    
    
    
    A()
    B()
    C()
    D()
    ~D()
    ~C()
    ~B()
    ~A()
    

面试问题: 怎么理解多重继承的?—重点

好处:可以更多的 代码复用
坏处: 菱形继承会出现 派生类 有多份 间接基类的数据 设计的问题

3.c++提供的四种类型转换

本节内容

  1. c中类型强转:
    int a = (int)b;

  2. cpp里的语言级别强转

    const_cast:
    	去掉常量属性的类型转换.
            
    static_cast:
    	能提供编译器认为安全的类型转换.---做有关联的类型转换 
    
    reinterpret_cast:
    	类似于c风格的强转.
            
    dynamic_cast:
    	主要用于继承结构中, 可以支持RTTIL类型识别的上下转换
    
  3. 代码:

    #include <iostream>
    using namespace std;
    
    
    class A 
    { 
    public:
        virtual void func() { cout << "~A()" << endl; }
        
    };
    
    
    class B : public A
    {
    
    public:
    
        void func() { cout << "~B()" << endl; }
    
    };
    
    class C : public A
    {
    
    public:
    
        void func() { cout << "~C()" << endl; }
    
        //添加新功能, 
        void funcss()
        {
            cout << "sssss" << endl;
        }
    
    };
    
    void classShow(A* p) //指针若是C类型, 就调用funcss, 而不是func
    {
        //typeid(*p).name()=="C",  不用这个, 用dynamic_cast
        //dynamic_cast会检查 p指针 是否指向的 是一个 C类型的对象
        // p->vfptr->vftable RTTI信息, 如果是, dynamic转型成功, 
        // 返回C对象的地址给 c2, 否则返回 nullptr
        //编译时期类型转换, 不能识别RTTI信息
    
        C* c2 = dynamic_cast<C*>(p);   //static_cast在这里永远都能强转成功
        if (c2 != nullptr)
        {
            c2->funcss();
        }
        else p->func();
    }
    
    int main() { 
    
        //const int a = 10;
        //**************************************************************************************************************
        //const_cast
        //int* p = (int*)&a;
        //int* p2 = const_cast<int*>(&a);  //二者在汇编上是一样的, 但是const_cast不能跨类型转换, 只能基本类型一样的转换
        /*
         int* p = (int*)&a;
    00D51FC7  lea         eax,[a]  
    00D51FCA  mov         dword ptr [p],eax  
    
        int* p2 = const_cast<int*>(&a);
    00D51FCD  lea         eax,[a]  
    00D51FD0  mov         dword ptr [p2],eax  
        */
    
        //double* p3 = (double*)&a;
        //double* p4 = const_cast<double*>(&a);  // 这就是不行的
        //*****************************************************************************************************************
        //static_cast----编译时期类型转换
        //int a = 10;
        //char b = static_cast<int>(a);
        //int* p = nullptr;
        //short* c = static_cast<short*>(p); // 只能做有联系的类型转换
        /*
    1. 什么是有关系的类型?
    
    继承关系:例如基类指针和派生类指针。
    
    基本类型的隐式转换:例如 int 到 double。
    
    用户定义的类型转换:例如类中定义了转换运算符。
    
    在这些情况下,static_cast 可以安全地使用。
    
    2. 什么是无关的类型?
    
    完全不同的指针类型:例如 int* 和 double*,char* 和 float* 等。
    
    不相关的类类型:例如两个没有继承关系的类。
        */
    
        //*****************************************************************************************************************
        //reinterpret_cast  类似与c风格, 啥都不管
    
        //*****************************************************************************************************************
        //dynamic_cast
        A a;
        B b;
        C c;
        classShow(&a);
        classShow(&b);
        classShow(&c);
        //结合classShow处看
        
    
    
    
        return 0;
    }
    
    
    
  4. 什么是 RTTI?

    RTTI 是 C++ 的一种特性,允许程序在运行时获取对象的类型信息。它主要由以下两部分组成:

    • typeid 运算符:用于获取对象的类型信息。
    • dynamic_cast 运算符:用于在继承层次中进行安全的类型转换。

    RTTI 需要编译器支持,并且在运行时需要额外的内存来存储类型信息。

### 施磊 C++ muduo 项目介绍 muduo是一个高性能的C++网络库,由陈硕开发并维护。此库广泛应用于高并发服务器程序的设计与实现中[^2]。 #### 主要特性 - **线程安全**:通过`shared_ptr`和`weak_ptr`解决多线程环境下共享资源的安全访问问题。 - **高效性能**:针对Linux平台进行了大量优化,能够处理大量的并发连接请求。 - **易于扩展**:提供了丰富的接口供开发者自定义协议解析逻辑和服务端业务流程控制。 #### 使用场景 适用于构建各种类型的分布式系统,特别是需要频繁通信的服务间交互模块。例如即时通讯软件、在线游戏后台等都需要依赖类似的框架来保证系统的稳定性和响应速度。 ```cpp // 创建TCP服务器实例 TcpServer server(loop, InetAddress(listenPort), "EchoServer"); server.setConnectionCallback( std::bind(&onConnection, _1)); server.setMessageCallback( std::bind(&onMessage, _1, _2, _3)); server.start(); loop.loop(); ``` 上述代码展示了如何利用muduo创建一个简单的回声(Echo) TCP Server,在这里注册了两个回调函数分别用于新连接建立以及接收到消息后的事件触发。 #### 学习路径建议 对于想要深入了解该项目的同学来说,可以从以下几个方面入手: - 阅读官方文档及注释说明; - 参考施磊老师的《C++课程笔记》系列资料中的相关内容部分[^1]; - 实践操作启动样例工程,熟悉基本命令行指令[^3];
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值