目录
虚函数
C++中的多态通过虚函数(virtual function)来实现
虚函数:被virtual修饰的成员函数
只要在父类中声明为虚函数,子类中重写的函数也自动变为虚函数(也就是说子类中可以省略virtual关键字)
虚函数的实现原理是虚表,这个虚表里面存储着最终需要调用的虚函数地址,这个虚表也叫虚函数表
class Animal{
public:
int age;
// void speak(){
// cout<<"Animal::speak()"<<endl;
// };
//
// void run(){
// cout<<"Animal:run()"<<endl;
// };
//只要有virtual就有虚函数就有虚表
virtual void speak(){
cout<<"Animal::speak()"<<endl;
};
virtual void run(){
cout<<"Animal:run()"<<endl;
};
// ~Animal(){
// cout<<"Animal:~Animal()"<<endl;
// }
virtual ~Animal(){
cout<<"Animal:~Animal()"<<endl;
}
};
class Cat:public Animal{
public:
int life;
void speak(){
cout<<"Cat::speak()"<<endl;
//调用父类的成员函数实现
Animal::speak();
}
void run(){
cout<<"Cat::run()"<<endl;
}
~Cat(){
cout<<"Cat::~Cat()"<<endl;
}
};
void virtualUsing(){
Cat * cat = new Cat();
//如果没有虚函数Cat对象的内存大小为8字节
//如果有虚函数Cat对象的内存大小为16字节,会增加一个指针
cout<<"sizeof(cat) is "<<sizeof(Cat)<<endl;
/**
(lldb) po cat
0x000000010054a3c0
对象cat的内存地址为:0x000000010054a3c0(堆内存)
(lldb) memory read/2xg 0x000000010054a3c0
0x10054a3c0: 0x000000010001d310 0x0000000a00000014
对象cat的内存分布
0-7字节存储了一个地址:0x000000010001d310 虚表地址
8-11字节存储age 20
12-15字节存储life 10
(lldb) memory read/2xg 0x000000010001d310
0x10001d310: 0x0000000100005aa0 0x0000000100005ae0
虚表地址内存分布
0-7字节存储speak方法地址
8-15字节存储run方法地址
LCClientDemo`tenDay::Cat::speak:
0x100005aa0 <+0>: pushq %rbp
LCClientDemo`tenDay::Cat::run:
0x100005ae0 <+0>: pushq %rbp
x86架构的内存分布图见:
catObjectVirtualTable.png
catObjectVirtualTable2.png
*/
cat->age = 20;
cat->life = 10;
cat->speak();
cat->run();
delete cat;
}
虚析构函数
含有虚函数的类,应该将析构函数声明为虚函数(虚析构函数)
delete父类指针时,才会调用子类的析构函数,保证析构的完整性
void virtualDestructorUsing(void){
Animal * cat = new Cat();
/**
当父类指针指向子类对象时,调用delete cat 时 只会调用父类的析构函数,
解决这个问题的方式是,将析构函数声明为虚函数;
*/
delete cat;
}
纯虚函数
纯虚函数:没有函数体且初始化为0的虚函数,用来定义接口规范
抽象类(Abstract Class)
含有纯虚函数的类,不可以实例化(不可以创建对象)
抽象类也可以包含非纯虚函数
如果父类是抽象类,子类没有完全实现纯虚函数,那么这个子类依然是抽象类
//纯虚函数相当于OC中的协议
class Person{
public:
virtual void speak() = 0;
virtual void run() = 0;
};
class Student : public Person{
public:
void speak(){
cout<<"Student::speak"<<endl;
}
void run(){
cout<<"Student::run"<<endl;
}
};
void pureVirtualFuncUsing(){
//抽象类 不可以初始化
//Variable type 'tenDay::Person' is an abstract class
//Person person;
//如果 speak 和 run 方法 没有完全实现 student初始化的时候会报错
//Variable type 'tenDay::Student' is an abstract class
Student stu;
stu.speak();
stu.run();
}
行者常至,为者常成!