《C++文章汇总》
上一篇介绍了引用和汇编《09-虚表、抽象类和多继承》,本文介绍菱形继承、虚继承、多继承作用和静态成员。
1.菱形继承
◼ 菱形继承带来的问题
最底下子类从基类继承的成员变量冗余、重复
最底下子类无法访问基类的成员,有二义性
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,重复了,如何解决这个问题呢?
2.虚继承
◼ 虚继承可以解决菱形继承带来的问题
◼ Person类被称为虚基类
虚表指针与本类起始的偏移量(一般是0)
虚基类第一个成员变量与本类起始的偏移量
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
结构体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;
}