美文网首页
010-菱形继承、虚继承、多继承作用和静态成员

010-菱形继承、虚继承、多继承作用和静态成员

作者: 一亩三分甜 | 来源:发表于2021-07-25 23:49 被阅读0次

    《C++文章汇总》
    上一篇介绍了引用和汇编《09-虚表、抽象类和多继承》,本文介绍菱形继承、虚继承、多继承作用和静态成员。

    1.菱形继承

    ◼ 菱形继承带来的问题
    最底下子类从基类继承的成员变量冗余、重复
    最底下子类无法访问基类的成员,有二义性


    image
    struct Person {
        int m_age;
    };
    struct Student:Person{
        int m_score;
    
    };
    struct Worker:Person{
        int m_salary;
    };
    struct Undergraduate:Student,Worker{
        int m_grade;
    };
    
    int main(){
        cout << sizeof(Undergraduate) << endl;
        //Undergraduate ug;
        //ug.m_age = 10;//二义性,分不清哪个m_age
        return 0;
    }
    //输出
    20
    

    菱形继承,则Undergraduate中就存在两个m_age,重复了,如何解决这个问题呢?


    图片.png

    2.虚继承

    ◼ 虚继承可以解决菱形继承带来的问题
    ◼ Person类被称为虚基类


    image

    虚表指针与本类起始的偏移量(一般是0)
    虚基类第一个成员变量与本类起始的偏移量


    图片.png
    struct Person {
        int m_age = 1;
    };
    struct Student: virtual Person{
        int m_score = 2;
    };
    struct Worker: virtual Person{
        int m_salary = 3;
    };
    struct Undergraduate:Student,Worker{
        int m_grade = 4;
    };
    int main(){
        cout << sizeof(Undergraduate) << endl;
        Undergraduate ug;
        ug.m_age = 10;
        cout << ug.m_age << endl;
        //ug.m_age = 10;//二义性,分不清哪个m_age
        return 0;
    }
    //输出
    40//存在两个虚表指针,每个指针8字节,结构体对齐原则,是8的整数倍
    10
    
    图片.png
    image

    结构体Undergraduate中最大的元素为两个虚表指针,Student的虚表指针和Worker的虚表指针,分别占用8个字节,则要满足结构体内存对齐,最后的内存大小要是8的整数倍,最后存放m_age的内存为4个字节,但要满足8字节对齐,最后一排也是8字节,总共是8+8+8+8+8 = 40字节
    使用虚继承可以节省内存空间

    //不用虚继承Undergraduate的大小 10 * 4 + 4 + 10 *4 +4 + 4 = 92
    //使用虚继承 8 + 4 + 8 + 4 + 4 + 10* 4 = 68(8字节对齐) = 72
    struct Person {
        int m_age = 1;
        int m_age1= 2;
        int m_age2= 3;
        int m_age3= 4;
        int m_age4= 5;
        int m_age5= 6;
        int m_age6= 7;
        int m_age7= 8;
        int m_age8= 9;
        int m_age9= 10;
    };
    struct Student:virtual Person{
        int m_score = 2;
    };
    struct Worker:virtual Person{
        int m_salary = 3;
    };
    struct Undergraduate:Student,Worker{
        int m_grade = 4;
    };
    int main(){
        cout << sizeof(Undergraduate) << endl;
        Undergraduate ug;
            return 0;
    }
    //输出
    72
    

    3.多继承的应用

    class JobBaomu{
    public:
        virtual void clean() = 0;
        virtual void cook() = 0;
    };
    class JobTeacher{
    public:
        virtual void playFootball() = 0;
        virtual void playBaseball() = 0;
    };
    class Student:public JobBaomu,public JobTeacher{
        int m_score;
    public:
        void clean(){
            
        }
        void cook(){
            
        }
        void playFootball(){
            
        }
        void playBaseball(){
            
        }
    };
    class Worker:public JobTeacher{
        int m_salary;
    public:
        void playFootball(){
            
        }
        void playBaseball(){
            
        }
    };
    int main(){
        /**
         兼职中心,招聘兼职,岗位如下:
         1.保姆:扫地、做饭
         2.老师:踢足球、打棒球
         应聘的角色如下:
         1.学生
         2.上班族
         3.护士
         4.医生
         5....
         */
        return 0;
    }
    

    4.静态成员(static)

    ◼ 静态成员:被static修饰的成员变量\函数
    可以通过对象(对象.静态成员)、对象指针(对象指针->静态成员)、类访问(类名::静态成员)
    ◼ 静态成员变量
    存储在数据段(全局区,类似于全局变量),整个程序运行过程中只有一份内存
    对比全局变量,它可以设定访问权限(public、protected、private),达到局部共享的目的
    必须初始化,必须在类外面初始化,初始化时不能带static,如果类的声明和实现分离(在实现.cpp中初始化)

    class Car{
    public:
        static int m_price;
        void run(){
            cout << "run()" << endl;
        }
    };
    //静态成员变量必须初始化,且只能在类的外部初始化
    int Car::m_price = 0;
    int main(){
        Car car1;
        car1.m_price = 100;
        
        Car car2;
        car1.m_price = 200;
        
        Car car3;
        car1.m_price = 300;
        
        cout << Car::m_price << endl;
        return 0;
    }
    //输出
    300
    

    内存在全局区,并不会因为对象的销毁而销毁

    class Car{
    public:
        static int m_price;
        void run(){
            cout << "run()" << endl;
        }
    };
    //静态成员变量必须初始化,且只能在类的外部初始化
    int Car::m_price = 0;
    int main(){
        Car car1;
        car1.m_price = 100;
        
        Car car2;
        car1.m_price = 200;
        
        Car car3;
        car1.m_price = 300;
        
        Car *car = new Car();
        car->m_price = 400;
        delete car;
        cout << Car::m_price << endl;
        return 0;
    }
    //输出
    400
    

    成员变量car内部访问,只占一份内存

    class Car{
    private:
        static int m_age;
    public:
        static int m_price;
        void run(){
            cout << "run()" << endl;
            m_age = 1;
        }
    };
    

    ◼ 静态成员函数
    内部不能使用this指针(this指针只能用在非静态成员函数内部)

    class Car{
    private:
        static int m_age;
    public:
        static int m_price;
        static void run(){
            cout << "run()" << endl;
        }
    };
    //静态成员变量必须初始化,且只能在类的外部初始化
    int Car::m_price = 0;
    int main(){
        Car ca;
        ca.run();
        Car *c = new Car();
        c->run();
        Car::run();
        return 0;
    }
    //输出
    run()
    run()
    run()
    

    不能是虚函数(虚函数只能是非静态成员函数)
    内部不能访问非静态成员变量\函数,只能访问静态成员变量\函数

    class Car{
    private:
        int m_age;
    public:
        static int m_price;
        static void run(){
            cout << "run()" << endl;
            m_age = 1;//不能访问
            test();//不能访问
        }
        void test(){
            m_age = 0;
            run();
        }
    };
    

    非静态成员函数内部可以访问静态成员变量\函数

    class Car{
    private:
        int m_age;
    public:
        static int m_price;
        static void run(){
            cout << "run()" << endl;
        }
        void test(){
            run();
            m_price = 10;
            m_age = 0;
            run();
        }
    };
    

    构造函数、析构函数不能是静态
    当声明和实现分离时,实现部分不能带static

    int Person::m_age = 0;
    class Student:Person{
    public:
        static int m_age;
    };
    int Student::m_age = 0;
    
    int main(){
        cout << &Person::m_age << endl;
        cout << &Student::m_age << endl;
        return 0;
    }
    //输出
    0x1000080c4
    0x1000080c8
    

    应用场景:统计有多少个Car对象,静态成员变量和静态成员函数配合使用

    class Car {
    private:
        //严格来说这里需要考虑多线程安全问题
        static int ms_count;
    public:
        Car(){
            cout << "Car()" << endl;
            ms_count ++;
        }
        ~Car(){
            cout << "~Car()" << endl;
            ms_count --;
        }
        static int getCount(){
            return ms_count;
        }
    };
    int Car::ms_count = 0;
    Car c;
    int main(){
        Car car;
        Car *ca = new Car();
        cout << Car::getCount() << endl;
        getchar();
        return 0;
    }
    //输出
    Car()
    Car()
    Car()
    3
    

    5.单例模式

    单例模式:设计模式的一种,保证某个类永远只创建一个对象
    1.构造\析构函数私有化
    2.定义一个私有的static成员变量指向唯一的那个单例对象
    3.提供一个公共的访问单例对象的接口

    class Rocket {
    private:
        Rocket(){};
        //写成私有,防止外部调用delete rocket指针
        ~Rocket(){};
        //用指针更加灵活,放在堆区,不用随时delete掉,没有直接用static Rocket ms_rocket对象放在全局区,始终存在
        static Rocket *ms_rocket;
    public:
        //这里要考虑多线程安全
        static Rocket *sharedRocket(){
            if (ms_rocket == NULL) {
                ms_rocket = new Rocket();
            }
            return ms_rocket;
        }
        //这里要考虑多线程安全
        static void deleteRocket(){
            if (ms_rocket != NULL) {
                delete ms_rocket;
                ms_rocket = NULL;
            }
        }
    };
    Rocket * Rocket::ms_rocket = NULL;
    int main(){
        Rocket *rocket1 = Rocket::sharedRocket();
        Rocket *rocket2 = Rocket::sharedRocket();
        Rocket *rocket3 = Rocket::sharedRocket();
        Rocket *rocket4 = Rocket::sharedRocket();
        
        cout << rocket1 << endl;
        cout << rocket2 << endl;
        cout << rocket3 << endl;
        cout << rocket4 << endl;
        return 0;
    }
    //输出
    0x1005333c0
    0x1005333c0
    0x1005333c0
    0x1005333c0
    

    5.delete *p作用,回收内存空间,该块内存空间可以被别人使用了,但内存空间不会清零,执行p = NULL操作该块内存空间也不会被清零,指针指针变量清零了

    int main(){
        int *p = new int;
        *p = 10;
        delete p;//回收内存空间,该块内存空间并不会被清零,可以重新被别人使用,内容被覆盖
          return 0;
    }
    

    相关文章

      网友评论

          本文标题:010-菱形继承、虚继承、多继承作用和静态成员

          本文链接:https://www.haomeiwen.com/subject/rckwmltx.html