继承
继承的方式有三种
访问权限 |
public |
protected |
private |
对本类 |
可见 |
可见 |
可见 |
对子类 |
可见 |
可见 |
不可见 |
对外部(调用方) |
可见 |
不可见 |
不可见 |
继承中的对象模型
- 父类中所有的非静态成员属性都会被子类继承下去
- 父类中私有成员属性是被编译器给隐藏了,因此访问不到,但确实被继承下去了
struct Person {
public:
int m_A;
protected:
int m_age;
private:
int m_B;
};
struct Student: public Person {
int m_no;
};
Student 对象占用16个字节
继承中构造和析构的顺序
- 构造顺序:先构造父类,再构造子类
- 析构:先析构子类,再析构父类
继承中访问同名的成员
- 子类对象可以直接访问子类中同名成员
- 子类对象加作用域可以访问父类同名成员
- 当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数
多继承
C++允许一个类可以有多个父类
class Student {
public:
int m_score;
void study() {
cout << "Student::study" << endl;
}
};
class Workder {
public:
int m_salary;
void work() {
cout << "Workder::work" << endl;
}con
};
class Undergraduate: public Student, public Workder {
public:
int m_grade;
void play() {
cout << "Undergraduate::play" << endl;
}
};
多继承体系下的构造函数调用
class Undergraduate: public Student, public Workder {
public:
int m_grade;
Undergraduate(int grade, int score, int salary): Student(score), Workder(salary) {
m_grade = grade;
}
};
## 多继承-虚函数
- 如果子类继承多个父类都有虚函数,那么子类对象就会产生对应的多张虚表
- ![image.png](https://img.haomeiwen.com/i5456635/938814dd437afbe3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/400)
## 同名函数,同名成员变量
```C++
class Student {
public:
int m_score;
int m_age;
};
class Workder {
public:
int m_salary;
int m_age;
};
class Undergraduate: public Student, public Workder {
public:
int m_grade;
int m_age;
Undergraduate(int grade, int score, int salary): Student(score), Workder(salary) {
m_grade = grade;
}
};
void test() {
Undergraduate ug(10, 10, 1);
ug.Student::eat();
ug.Workder::eat();
ug.eat();
ug.Student::m_age = 29;
ug.Workder::m_age = 29;
ug.m_age = 29;
}
菱形继承
- 菱形继承带来的问题
- 最低下子类从基类继承的成员变量冗余,重复
- 最低子类无法访问基类的成员,有二义性
-
image.png
class Person {
int m_age;
};
class Student: public Person {
public:
int m_score;
};
class Workder: public Person {
public:
int m_salary;
};
class Undergraduate: public Student, public Workder {
public:
int m_grade;
};
虚继承
- 虚继承可以解决菱形继承问题
- Person称为虚基类
- Workder的虚表存储的内容有
- 虚表指针与本类起始的偏移量(一般是0)
- 虚基类第一个成员与本类起始的偏移量
image.png
class Student: virtual public Person {
public:
int m_score;
};
class Workder: virtual public Person {
public:
int m_salary;
};
class Undergraduate: public Student, public Workder {
public:
int m_grade;
};
image.png
image.png
class Sleep: virtual public Animal { };
class Tuo: virtual public Animal { };
class SleepTuo: public Sleep, public Tuo { };
/*
Sleep 和 Tuo中通过虚继承存储的是一个vbptr(虚基类指针),该指针分别指向一个Sleep和Tuo的一个虚表,该虚表存储的是SleepTuo中一个地址偏移量
,通过该偏移量和vbptr的起始地址,得到最终指向的地址
Sleep的vbptr指向: 0(起始值)+ 8(偏移量)= 8
Tuo的vbptr指向: 4(起始值)+ 4(偏移量) = 8
*/
静态成员
- 被 static修饰的成员变量\函数
- 可以通过对象(对象.静态成员),对象指针(对象指针->静态成员), 类访问(类名::静态成员)
- 静态成员变量
- 存储在数据段(全局区,类似于全局变量),整个程序运行过程中只有一份内存
- 对比全局变量,它可以设定访问权限(public, protected, private),达到局部共享的目的
- 必须初始化,必须在类外部初始化,初始化不能带static,如果类的声明和实现分离(在实现的.cpp中初始化)
- 静态成员函数
- 内部不能使用this指针(this指针只能用在非静态成员函数内部)
- 不能是虚函数(虚函数只能是非静态成员函数)
- 内部不能访问非静态成员变量\函数,只能访问静态成员变量\函数
- 非静态成员函数内部可以访问静态成员变量\函数
- 构造函数,析构函数不能是静态
- 当声明和实现分离时,实现不能带static
静态成员的访问方式
Perosn::m_age = 30;
Perosn p;
p.m_age = 0;
class Car {
int m_price;
static int ms_count;
public:
static int getCount() {
return ms_count;
}
Car(int price = 0): m_price(price) {
ms_count++;
}
~Car() {
}
};
静态成员经典应用 - 单例模式
class Rocket {
private:
static Rocket* ms_instace;
Rocket() { }
Rocket(const Rocket &rocket) { };
Rocket &operator=(const Rocket &rocket) { return *this; };
public:
static Rocket* shared() {
// 严格要讲,这段代码需要考虑线程安全的问题
if (ms_instace == nullptr) {
ms_instace = new Rocket();
}
return ms_instace;
}
};
Rocket* Rocket::ms_instace = nullptr;
const成员
- const成员: 被const修饰的成员变量,非静态成员函数
- const成员变量
- 必须初始化(类内部初始化),可以在声明的时候直接初始化赋值
- 非static的const成员变量还可以在初始化列表中初始化
- const成员函数(非静态)
- const关键字写在参数列表后面,函数的声明和实现必须带const
- 内部不能修改费static成员变量
- 内部只能调用 const 成员函数、 static 成员函数
- 非const成员函数可以调用const成员函数
- const成员函数和非const成员函数构成重载
- 非const对象(指针)优先调用非const成员函数
- const对象只能调用const成员函数,static函数
- this指针的本质是指针常量,指针的指向不可以修改的
- 在成员函数后面添加const,修饰的是this指向,让指针指向的值不可以修改, 常函数内不可以修改成员属性
- 要在常函数中修改成员变量,可以给这个成员变量加上mutable关键字
class Car {
const int mc_wheelsCount = 20;
public:
Car(): mc_wheelsCount(10) {};
void run() const {
std::cout << "run()" << std::cout;
}
};
引用类型成员
- 引用类型成员变量必须初始化(不考虑static情况)
class Car {
int age;
int m_price = age;
public:
Car(int &price): m_price(price) {};
};
网友评论