《C++文章汇总》
上一篇介绍了引用和汇编《08-初始化列表、父类构造函数、虚函数和多态》,本文介绍虚表、抽象类和多继承。
1.虚表
◼ 虚函数的实现原理是虚表,这个虚表里面存储着最终需要调用的虚函数地址,这个虚表也叫虚函数表
struct Animal{
int m_age;
virtual void speak(){
cout << "Animal::speak()" << endl;
}
virtual void run(){
cout << "Animal::run()" << endl;
}
};
struct Dog:Animal{
void speak(){
cout << "Dog::speak()" << endl;
}
void run(){
cout << "Dog::run()" << endl;
}
};
struct Cat:Animal{
int m_life;
void speak(){
cout << "Cat::speak()" << endl;
}
void run(){
cout << "Cat::run()" << endl;
}
};
struct Pig:Animal{
void speak(){
cout << "Pig::speak()" << endl;
}
void run(){
cout << "Pig::run()" << endl;
}
};
void liu(Animal *p){
p->speak();
p->run();
}
int main(){
Animal *cat = new Cat();
cat->m_age = 20;
cat->speak();
cat->run();
cat = new Pig();
cat->speak();
cat->run();
return 0;
}
//输出
Cat::speak()
Cat::run()
Pig::speak()
Pig::run()
x86环境的虚表图
image
虚表汇编分析
//调用speak
Animal *cat = new Cat();
cat->speak();
//ebp-8是指针变量cat的地址
//eax存储的是cat对象的地址
mov eax,dword ptr [ebp-8]
//取出Cat对象的最前面的4个字节(虚表地址)给edx
//取出虚表的地址值给edx
mov edx,dword ptr [eax]
//取出虚表的最前面的4个字节给eax
//取出Cat::speak的函数调用地址给eax
mov eax,dword ptr [edx]
//call Cat::speak
call eax
//调用run
Animal *cat = new Cat();
cat->run();
//ebp-8是指针变量cat的地址
//eax存储的是Cat对象的地址
mov eax,dword ptr [ebp-8]
//取出Cat对象的最前面的4个字节(虚表地址)给edx
mov edx,dword ptr [eax]
//跳过虚表的最前面4个字节,取出4个字节赋值给eax
//取出Cat::run的函数调用地址给eax
mov eax,dword ptr [edx+4]
//call Cat::run
call eax
虚表内存分析
//Cat对象的地址
0x016DDB98
//虚表地址
0x00259b34
//虚表的内容
0x00251569
0x00251550
◼ 所有的Cat对象(不管在全局区、栈、堆)共用同一份虚表
子类中没有方法时,会调用父类中virtual修饰的方法,放入子类对象(cat对象)的前4个字节内存地址中
struct Animal{
int m_age;
virtual void speak(){
cout << "Animal::speak()" << endl;
}
virtual void run(){
cout << "Animal::run()" << endl;
}
};
struct Dog:Animal{
void speak(){
cout << "Dog::speak()" << endl;
}
void run(){
cout << "Dog::run()" << endl;
}
};
struct Cat:Animal{
int m_life;
// void speak(){
// cout << "Cat::speak()" << endl;
// }
void run(){
cout << "Cat::run()" << endl;
}
};
struct Pig:Animal{
// void speak(){
// cout << "Pig::speak()" << endl;
// }
void run(){
cout << "Pig::run()" << endl;
}
};
void liu(Animal *p){
p->speak();
p->run();
}
int main(){
Animal *cat = new Cat();
cat->m_age = 20;
cat->speak();
cat->run();
cat = new Pig();
cat->speak();
cat->run();
return 0;
}
//输出
Animal::speak()
Cat::run()
Animal::speak()
Pig::run()
图片.png
父类没有子类继承,有virtual修饰的方法函数,则也存在虚表,调用时会从虚表中查找调用函数的地址
struct Animal{
int m_age;
virtual void speak(){
cout << "Animal::speak()" << endl;
}
virtual void run(){
cout << "Animal::run()" << endl;
}
};
int main(){
Animal *ani = new Animal();
ani->m_age = 20;
ani->speak();
ani->run();
}
子类中不存在virtual修饰的函数,仍存在虚表
struct Animal{
int m_age;
virtual void speak(){
cout << "Animal::speak()" << endl;
}
virtual void run(){
cout << "Animal::run()" << endl;
}
};
struct Cat:Animal{
int m_life;
void speak(){
cout << "Cat::speak()" << endl;
}
};
struct WhiteCat:Cat{
};
int main(){
Animal *ani = new WhiteCat();
ani->speak();
ani->run();
return 0;
}
//输出
Cat::speak()
Animal::run()
若虚函数写在子类中,则从子类开始有虚表
struct Animal{
int m_age;
void speak(){
cout << "Animal::speak()" << endl;
}
void run(){
cout << "Animal::run()" << endl;
}
};
struct Cat:Animal{
int m_life;
virtual void speak(){
cout << "Cat::speak()" << endl;
}
virtual void run(){
cout << "Cat::run()" << endl;
}
};
struct WhiteCat:Cat{
void speak(){
cout << "WhiteCat::speak()" << endl;
}
void run(){
cout << "WhiteCat::run()" << endl;
}
};
int main(){
Animal *ani = new WhiteCat();
ani->speak();
ani->run();
Cat *cat = new WhiteCat();
cat->speak();
cat->run();
return 0;
}
//输出
Animal::speak()
Animal::run()
WhiteCat::speak()
WhiteCat::run()
子类的函数是虚函数,要么是从父类重写,要么是自己在函数名前加virtual
2.调用父类的成员函数
先执行父类的重写的虚函数,C++没有super关键字,用父类的类名调用
struct Animal{
int m_age;
virtual void speak(){
cout << "Animal::speak()" << endl;
}
virtual void run(){
cout << "Animal::run()" << endl;
}
};
struct Cat:Animal{
int m_life;
void speak(){
Animal::speak();
cout << "Cat::speak()" << endl;
}
void run(){
cout << "Cat::run()" << endl;
}
};
int main(){
Animal *ani = new Cat();
ani->speak();
ani->run();
return 0;
}
//输出
Animal::speak()
Cat::speak()
Cat::run()
3.虚析构函数
◼ 如果存在父类指针指向子类对象的情况,应该将析构函数声明为虚函数(虚析构函数)
delete父类指针时,才会调用子类的析构函数,保证析构的完整性,先调用子类析构函数后调用父类析构函数
struct Animal{
int m_age;
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;
}
};
struct Cat:Animal{
int m_life;
void speak(){
cout << "Cat::speak()" << endl;
}
void run(){
cout << "Cat::run()" << endl;
}
Cat(){
cout << "Cat::Cat()" << endl;
}
~Cat(){
cout << "Cat::~Cat()" << endl;
}
};
int main(){
Animal *ani = new Cat();
delete ani;
return 0;
}
//输出
Animal::Animal()
Cat::Cat()
Cat::~Cat()
Animal::~Animal()
4.纯虚函数
◼ 纯虚函数:没有函数体且初始化为0的虚函数,用来定义接口规范
struct Animal{
virtual void speak() = 0;
virtual void run() = 0;
};
struct Dog:Animal{
void speak(){
cout << "Dog::speak()" << endl;
}
void run(){
cout << "Dog::run()" << endl;
}
};
struct Cat:Animal{
void speak(){
Animal::speak();
cout << "Cat::speak()" << endl;
}
void run(){
cout << "Cat::run()" << endl;
}
};
struct Pig:Animal{
void speak(){
cout << "Pig::speak()" << endl;
}
void run(){
cout << "Pig::run()" << endl;
}
};
int main(){
Animal *ani = new Pig();
ani->speak();
ani->run();
getchar();
return 0;
}
//输出
Pig::speak()
Pig::run()
5.抽象类(Abstract Class)
含有纯虚函数的类,不可以实例化(不可以创建对象)
抽象类也可以包含非纯虚函数、成员变量
struct Animal{
int m_age;
virtual void speak() = 0;
virtual void run() = 0;
void eat(){}
};
如果父类是抽象类,子类没有完全重写纯虚函数,那么这个子类依然是抽象类
//抽象类
struct Animal{
int m_age;
virtual void speak() = 0;
virtual void run() = 0;
void eat(){
}
};
//抽象类
struct Dog:Animal{
void speak(){
cout << "Dog::speak()" << endl;
}
};
//非抽象类
struct HaShiQi:Dog{
void speak(){
cout << "HaShiQi::speak()" << endl;
}
void run(){
cout << "HaShiQi::run()" << endl;
}
};
6.多继承
◼ C++允许一个类可以有多个父类(不建议使用,会增加程序设计复杂度)
image
struct Student{
int m_score;
void study(){
cout << "Student::study() - score = " << m_score << endl;
}
};
struct Worker{
int m_salary;
void work(){
cout << "Worker::work() - salary = " << m_salary << endl;
}
};
struct Undergraduate:Student,Worker{
int m_grade;
void play(){
cout << "Undergraduate::play() - m_grade = " << m_grade << endl;
}
};
int main(){
Undergraduate *u = new Undergraduate();
u->m_score = 100;
u->m_salary = 2000;
u->m_grade = 4;
u->work();
u->study();
u->play();
return 0;
}
//输出
Worker::work() - salary = 2000
Student::study() - score = 100
Undergraduate::play() - m_grade = 4
7.多继承体系下的构造函数调用
struct Student{
int m_score;
Student(int score):m_score(score){}
void study(){
cout << "Student::study() - score = " << m_score << endl;
}
};
struct Worker{
int m_salary;
Worker(int salary):m_salary(salary){}
void work(){
cout << "Worker::work() - salary = " << m_salary << endl;
}
};
struct Undergraduate:Student,Worker{
int m_grade;
Undergraduate(int grade,int score,int salary):m_grade(grade),Student(score),Worker(salary){}
void play(){
cout << "Undergraduate::play() - m_grade = " << m_grade << endl;
}
};
int main(){
Undergraduate *u = new Undergraduate(4,100,2000);
cout << u->m_grade << endl;
cout << u->m_score << endl;
cout << u->m_salary << endl;
return 0;
}
//输出
4
100
2000
8.多继承-虚函数
◼ 如果子类继承的多个父类都有虚函数,那么子类对象就会产生对应的多张虚表
图片.png
9.同名函数
struct Student{
int m_score;
void eat(){
cout << "Student::eat()" << endl;
}
};
struct Worker{
int m_salary;
void eat(){
cout << "Worker::eat()" << endl;
}
};
struct Undergraduate:Student,Worker{
int m_grade;
void eat(){
cout << "Undergraduate::eat()" << endl;
}
};
int main(){
Undergraduate ug;
ug.eat();
ug.Student::eat();
ug.Worker::eat();
ug.Undergraduate::eat();
return 0;
}
//输出
Undergraduate::eat()
Student::eat()
Worker::eat()
Undergraduate::eat()
若子类没有eat(),直接调用eat()会产生二义性报错
10.同名成员变量
struct Student{
int m_age;
};
struct Worker{
int m_age;
};
struct Undergraduate:Student,Worker{
int m_age;
};
int main(){
cout << sizeof(Undergraduate) << endl;
Undergraduate ug;
ug.m_age = 10;
cout << ug.m_age << endl;
ug.Student::m_age = 20;
ug.Worker::m_age = 23;
cout << ug.Student::m_age << endl;
cout << ug.Worker::m_age << endl;
return 0;
}
//输出
12
10
20
23
image
网友评论