• C++继承体系中的内存分段


    ———————————————综述与目录——————————————

    讨论这个问题之前我们先明确类的结构,一个类的大概组成,下面的很多分类名词都是我个人杜撰,为的就是让读者看懂能够区分,下面分别分类:

    目录

    空类 不含任何成员变量,也不继承某个基类。
    结构类 像C语言中结构体一样,要么只包含基本数据类型,要么是其他构造类型的嵌套,或者两者兼而有之。
    派生类 有至少一个基类。
    多态类 本身是一个派生类,且基类中有虚函数。

    以上只是大概的分类,细分会在下面的情况里讨论,该文章仅当作为个人使用C++经验的总结,不作学习参考,因为归纳与分类这个事情,学会了自然通。

     

     ———————————————进入正题——————————————

     空类:

    空类不空,虽然不含任何成员,但必须用一个占位符描述,站在逻辑的角度来考虑,因为其存在的合法性,必须在语法上保证正确性,如果构建一个空类的数组。试想没有大小类构成的数组,又怎么去偏移地址来寻找下一个元素呢!

    结构类:

    其内存分布与C语言中的结构体没有任何区别。

    派生类:

    派生表现的是一种继承关系。派生类中一定有父类的共性,也有自己的个性。表现出的是一种继承与发展的关系。刚从C语言转到C++时转不过弯来常常自问为什么要继承呢,都写到一个类里不行吗?后来明白如果没有继承便少了一种代码重用方式。关于派生类的内存对齐方式,我前面的随笔中有写:https://www.cnblogs.com/wangkeqin/p/12861852.html

    要讨论派生类的内存分段,就要从赋值兼容说起,这里先明确定义:赋值兼容规则是指,在需要基类对象的任何地方,都可以使用公有派生类的对象来替代。

     

    赋值兼容细化

    1 派生类的对象可以赋值给基类对象。
    2 派生类的对象可以初始化基类的引用。
    3 派生类对象的地址可以赋给指向基类的指针。

     

    派生类转化成基类是一种类型安全的转化,对象赋值不必讨论。主要是看指针和引用。

    #include <iostream>
    
    using namespace std;
    
    class P1
    {
    public:
        P1():m_p1(0){
            cout<<"P1():"<<this<<endl;
        }
        ~P1(){
            cout<<"~P1():"<<this<<endl;
        }
        void* PrintP1(){
            cout<<"P1_Prinit:"<<this<<endl;
            return this;
        }
    protected:
        int m_p1;
    };
    
    class P2
    {
    public:
        P2():m_p2(0){
            cout<<"P2():"<<this<<endl;
        }
        ~P2(){
            cout<<"~P2():"<<this<<endl;
        }
        void* PrintP2(){
            cout<<"P1_Prinit:"<<this<<endl;
            return this;
        }
    protected:
        int m_p2;
    };
    
    class Son:public P1,public P2
    {
    public:
        Son():m_s1(0){
            cout<<"Son():"<<this<<endl;
        }
        ~Son(){}
        void* PrintSon()
        {
            cout<<"Son_Print:"<<this<<endl;
            return this;
        }
    
    protected:
        int m_s1;
    };
    
    int main()
    {
        cout<<"---------create a son instance--------------"<<endl;
        Son s1;
    
        cout<<"-----------pointer compare------------------"<<endl;
        P1 * p1Son = &s1;
        P2 * p2Son = &s1;
        if((long long)p1Son == (long long)p2Son)
            cout<<"p1Son==p2Son"<<endl;
        else
            cout<<"p1Son:"<<p1Son<<"----"<<"p2Son:"<<p2Son<<endl;
        cout<<"--------reference pointer compare-----------"<<endl;
        P1 & r1Son = s1;
        P2 & r2Son = s1;
    
        if((long long)(&r1Son) == (long long)(&r2Son))
            cout<<"r1Son==r2Son"<<endl;
        else
            cout<<"pr1Son:"<<(&r1Son)<<"----"<<"pr2Son:"<<(&r2Son)<<endl;
    
        return 0;
    }

     运行结果:

     不同的基类指针指向同一个派生类对象,其指针的值不同,也就是说不同基类指针在接受派生类对象赋值时,所指向的派生类的“段”是不一样的。基类引用派生类对象同理。

     多态类:

    多态类的讨论实际上就是虚函数表的继承与合并问题,情况比较多,放在这篇文章中讨论:https://www.cnblogs.com/wangkeqin/p/12887739.html

     

     

     

  • 相关阅读:
    English trip V2-B 14 Yes, I can! 是的,我能! Teacher:Russell
    I1-3 Weather Teacher:Corrine
    4-redis数据过期策略
    redis持久化
    redis优势
    解决error while loading shared libraries
    1-zookeeper基本原理和使用
    ObjectiveSQL框架让你不要再写复杂SQL
    sharding-proxy+sharding-ui安装使用配置
    vim 多行取消注释
  • 原文地址:https://www.cnblogs.com/wangkeqin/p/12873051.html
Copyright © 2020-2023  润新知