1. 面向对象三大特性-继承和封装
1.1 继承
1.2 封装
- 什么是封装
- 狭义上:成员变量属性私有化,对外提供get set方法。即类的成员变量 函数化,自定义函数体,访问安全
- 广义上:实现特定功能的过程,抽取为函数或类中的方法,需要时候直接调用,避免重复造轮子。
- 为什么要有封装?
2. 多态
2.1 什么多态?
- 一个操作用于不同的对象,可以有不同的解释,实现一个函数顶多个函数的作用
- 通俗点说:在运行时识别真正的对象模型,调用对应子类中的函数。
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210224190453740.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xJQU5HRkFOR1dFSQ==,size_16,color_FFFFFF,t_70)
void liu(Animal* p) {
p->run();
p->speak();
}
2.2 实现多态的前提
2.3 为什么父类指针可以指向子类对象?
- 父类指针指向子类对象是不安全。根据指针指向的Person类型,指针变量可访问m_age变量,同时堆空间内存Student对象存在m_age属性。访问m_age时只会修改属于student对象空间的值。不会修改别人空间的值,所以是安全的,允许这样操作。
Person *p=new Student();
p->m_age = 30;
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210224170935721.png)
- 子类指针指向父类对象是不安全。根据指针指向的Student类型,指针变量可访问m_age和m_no变量,但堆空间内存Person对象 只有m_age。访问m_no时会修改不属于person对象空间的值。可能会修改别人空间的数据,所以是不安全的
- person类型,使用指针可以访问m_age,同时堆空间student对象存在m_age ,所以是安全。
Student* s=(Student*)new Person();
s->m_age;
s->m_no;
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210224171838338.png)
2.4 为什么要有多态?
- 提高代码的扩展性。多态中父类指针指向子类对象,参数传哪个子类对象就调用哪个子类的函数,实现同一份代码有不同的结果,提高代码的扩展性。
- 例1:以后再需要遛羊,就需要再写一个liu(羊)的函数,代码扩展性很差
- 例2:java框架中,参数使用父类对象,自定义类继承父类重写父类方法,即可根据自己逻辑对代码进行扩展
struct Dog {
void speak() {
cout << "Dog::speak()" << endl;
}
void run() {
cout << "Dog::run()" << endl;
}
};
struct Cat {
void speak() {
cout << "Cat::speak()" << endl;
}
void run() {
cout << "Cat::run()" << endl;
}
};
struct Pig {
void speak() {
cout << "Pig::speak()" << endl;
}
void run() {
cout << "Pig::run()" << endl;
}
};
void liu(Dog* p) {
p->run();
p->speak();
}
void liu(Cat* p) {
p->run();
p->speak();
}
void liu(Pig* p) {
p->run();
p->speak();
}
int main() {
liu(new Dog());
liu(new Cat());
liu(new Pig());
}
- 怎么办?多态 子类继承父类,使用父类指针指向子类对象,传什么对象 调什么对象的方法,实现一个函数顶多个函数的作用。
struct Pig :public Animal {
void speak() {
cout << "Pig::speak()" << endl;
}
void run() {
cout << "Pig::run()" << endl;
}
};
void liu(Animal* p) {
p->run();
p->speak();
}
int main() {
liu(new Dog());
liu(new Cat());
liu(new Pig());
}
2.5 多态怎么实现的?
- c++中的多态通过虚函数(virtual)来实现
- 只要在父类中声明为虚函数,子类中重写的函数也自动变为虚函数,这样在调用的时候就可以实现多态
struct Animal {
virtual void speak() {
cout << "Animal::speak()" << endl;
}
virtual void run() {
cout << "Animal::run()" << endl;
}
};
struct Dog:public Animal {
void speak() {
cout << "Dog::speak()" << endl;
}
void run() {
cout << "Dog::run()" << endl;
}
};
struct Pig :public Animal {
void speak() {
cout << "Pig::speak()" << endl;
}
void run() {
cout << "Pig::run()" << endl;
}
};
void liu(Animal* p) {
p->run();
p->speak();
}
int main() {
liu(new Dog());
liu(new Pig());
}
2.6 虚函数的实现原理?
- 子类对象空间的前4个字节存储虚表的地址
- 多态调用对象函数时,根据虚表存储的地址 找到虚表空间
- 虚表空间中存储虚函数的地址
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210225123449218.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xJQU5HRkFOR1dFSQ==,size_16,color_FFFFFF,t_70)
struct Animal {
int age;
virtual void speak() {
cout << "Animal::speak()" << endl;
}
virtual void run() {
cout << "Animal::run()" << endl;
}
};
struct Cat :public Animal {
int life;
void speak() {
cout << "Cat::speak()" << endl;
}
void run() {
cout << "Cat::run()" << endl;
}
};
int main() {
Animal* p = new Cat();
p->age = 20;
p->run();
p->speak();
}
![在这里插入图片描述](https://img-blog.csdnimg.cn/20210225114931549.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0xJQU5HRkFOR1dFSQ==,size_16,color_FFFFFF,t_70)
49: p->speak();
mov eax,dword ptr [p]
mov edx,dword ptr [eax]
mov eax,dword ptr [edx]
call eax