您的位置:首页 > 汽车 > 新车 > 武汉市新闻发布会_上海比较好的公关公司_重庆seo软件_seo代码优化包括哪些

武汉市新闻发布会_上海比较好的公关公司_重庆seo软件_seo代码优化包括哪些

2025/3/15 8:08:01 来源:https://blog.csdn.net/weixin_65612492/article/details/146267645  浏览:    关键词:武汉市新闻发布会_上海比较好的公关公司_重庆seo软件_seo代码优化包括哪些
武汉市新闻发布会_上海比较好的公关公司_重庆seo软件_seo代码优化包括哪些

继承与多态-多重继承

文章目录

  • 继承与多态-多重继承
    • 1.虚基类和虚继承
      • 本节内容
    • 2.菱形继承---怎么解决?
      • 本节内容
      • **面试问题: 怎么理解多重继承的?**---重点
    • 3.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}      -----  重点 :  指向vbtable4      | mb+---+--- (virtual base A)   ----  这里开始是A8      | ma+---B::$vbtable@:0      | 01      | 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\   /\ /DA/|\/ | \B  C  D\ | /\|/EA/ \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_castA a;B b;C c;classShow(&a);classShow(&b);classShow(&c);//结合classShow处看return 0;
    }
  4. 什么是 RTTI?

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

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

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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com