17. 虚析构函数再谈动态绑定多态到底是啥抽象类

Posted 为了财务自由!

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了17. 虚析构函数再谈动态绑定多态到底是啥抽象类相关的知识,希望对你有一定的参考价值。

  1. 虚函数能产生函数地址!存储在虚函数表
  2. 对象必须存在,因为只有对象,才存在虚函数指针、表、虚函数地址

构造函数完成,对象才产生!
构造函数不能是virtual,构造函数调用虚函数,不会发生静态绑定!(构造函数中,调用的任何函数都是静态绑定的!)
派生类对象构造过程:先调用父类构造函数,再调用子类构造函数!

virtual+static是不行的,因为静态方法不依赖对象!(联想到第二点!)

//如果父类析构函数不是虚函数:
Base *pb = new Derive(10);
pb->show(); //  动态绑定 pb Base*  *pb Derive
delete pb; // 派生类的析构函数没有被调用到!!!

编译器发现pb是个Base*类型,去Base类找,析构函数是个普通的函数,那么就是静态绑定了!直接调用Base!所以需要定义成虚析构函数!

那么虚析构函数,子类的析构函数在虚函数表中会把父类的虚析构函数覆盖掉,那么就先执行子类的析构函数了,由于继承,子类有父类的成员变量,那么子类会想办法析构父类,就调用了父类的析构函数!

什么时候把基类的析构函数必须实现成虚函数?
基类的指针(引用)指向堆上new出来的派生类对象的时候, delete pb(基类的指针)
,它调用析构函数的时候,必须发生动态绑定,否则会导致派生类的析构函数无法调用

虚函数动态绑定问题
是不是虚函数的调用一定就是动态绑定?不是的!
在类的构造函数当中,调用虚函数,也就是静态绑定(构造函数中调用其他虚函数,不会发生动态绑定!因为只有对象了,才有虚函数指针和表,构造函数执行完才有对象!)

如果不是通过指针或者引用变量来调用虚函数,那就是静态绑定

如何解释多态

// 动物的基类
class Animal

public:
	Animal(string name) :_name(name) 
	virtual void bark() 
protected:
	string _name;
;
// 以下是动物实体类
class Cat : public Animal

public:
	Cat(string name) :Animal(name) 
	void bark()  cout << _name << " bark: miao miao!" << endl; 
;
class Dog : public Animal

public:
	Dog(string name) :Animal(name) 
	void bark()  cout << _name << " bark: wang wang!" << endl; 
;
class Pig : public Animal

public:
	Pig(string name) :Animal(name) 
	void bark()  cout << _name << " bark: heng heng!" << endl; 
;

void bark(Animal *p)

	p->bark(); // Animal::bark虚函数,动态绑定了 
	/*
	下面的一组bark API接口无法做到我们软件涉及要求的“开-闭“原则
	软件设计由六大原则   “开-闭“原则  对修改关闭,对扩展开放
	p->cat Cat vftable &Cat::bark
	p->dog Dog vftable &Dog::bark
	p->pig Pig vftable &Pig::bark
	*/


派生类继承了基类,对bark重写,那么虚函数表继承自基类的bark函数地址就被覆盖了。

继承的好处:
可以做代码的复用!

抽象类和普通类有什么区别?
一般把什么类涉及成抽象类?

/*
定义Animal的初衷,并不是让Animal抽象某个实体的类型
1.string _name; 让所有的动物实体类通过继承Animal直接复用该属性
2.给所有的派生类保留统一的覆盖/重写接口

拥有纯虚函数的类,叫做抽象类!(Animal)
Animal a; No!!! 
抽象类不能再实例化对象了,但是可以定义指针和引用变量
*/
class Animal 

public:
	Animal(string name) :_name(name) 
	// 纯虚函数
	virtual void bark() = 0;
protected:
	string _name;
;
// 以下是动物实体类
class Cat : public Animal

public:
	Cat(string name) :Animal(name) 
	void bark()  cout << _name << " bark: miao miao!" << endl; 
;
class Dog : public Animal

public:
	Dog(string name) :Animal(name) 
	void bark()  cout << _name << " bark: wang wang!" << endl; 
;
class Pig : public Animal

public:
	Pig(string name) :Animal(name) 
	void bark()  cout << _name << " bark: heng heng!" << endl; 
;
void bark(Animal *p)

	p->bark(); // Animal::bark虚函数,动态绑定了 

再举个例子:

//抽象类
class Car

	Car(string name,double oil)
		:_name(name),_oil(oil)
	
	double getLeftMiles()
	
		return oil * getMilesPerGalloc();//一加仑跑的公里数
		//但是this->getMilesPerGalloc()动态绑定!
	
protected:
	string _name;
	double _oil;
	virtual double getMilesPerCallon() = 0;//纯虚函数
;
class Bnze : public Car

public:
	Bnze(string name,double oil) : Car(name,oil)
	
	double getMilesPerCallon()
	
		return 20.0;
	
;
class Audi: public Car

public:
	Audi(string name,double oil) : Car(name,oil)
	
	double getMilesPerCallon()
	
		return 18.0; 
	
;
class BMW: public Car

public:
	Audi(string name,double oil) : Car(name,oil)
	
	double getMilesPerCallon()
	
		return 19.0; 
	
;
//给外部提供一个统一的获取汽车剩余路程数的API
void showCarLeftMiles(Car& car)

	//静态绑定 调用的是基类的函数!
	cout << car.getName()<<"left miles:"<<car.getLeftMiles()<<"公里";
;
int main()

	Bnze b1("奔驰",20.0);
	Audi a("奥迪",20.0);
	BMW b2("宝马",20.0);
	showCarLeftMiles(b1);
	showCarLeftMiles(a);
	showCarLeftMiles(b2);

以上是关于17. 虚析构函数再谈动态绑定多态到底是啥抽象类的主要内容,如果未能解决你的问题,请参考以下文章

C++中的各种“虚“-- 虚函数纯虚函数虚继承虚基类虚析构纯虚析构抽象类讲解

C++中基类的析构函数为什么要用virtual虚析构函数

基类的析构函数写成virtual虚析构函数

为多态基类声明一个虚析构函数

C++虚函数总结

c++ 深入理解虚函数