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

2021-07-25  本文已影响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;
}
上一篇 下一篇

猜你喜欢

热点阅读