1. 多态的虚函数的意义
1 案例:父类和子类有同名函数,但是功能不一样,但是同时,子类又继承了父类,就会导致调用的错误,想调用子类的同名函数,
但是在某些情况下,会错误调用父类的同名函数,比如
- 情形一:
	//父类指针可以指向子类对象
	cout <<"----- Father* men[] = { &father, &son1, &son2 }; -----" << endl;
	Father* men[] = { &father, &son1, &son2 };
	party(men, sizeof(men)/ sizeof(men[0]));
- 情形二:
	//这段函数的本意是想调用儿子的paly函数,但是实际上调用的是父类的
	Father* p;
	p = &son1;
	p->play();

- 上述问题的解决方案就是在父类里面使用虚基类,只需要在父类的同名函数前面加上virtual关键字即可,子类的同名函数可加可不加,再次运行结果如下
#pragma once
class Father
{
public:
	virtual void play();
};

son 函数
#pragma once
#include "Father.h"
class Son:public Father
{
public:
	virtual void play();
};
#include "Son.h"
#include <iostream>
#include "string"
using namespace std;
void Son::play()
{
	cout << "Game" << endl;
}
——————————————————————————————————————————————————————
Father函数
#pragma once
class Father
{
public:
	void play();
};
#include "Father.h"
#include <iostream>
#include "string"
using namespace std;
void Father::play()
{
	cout << "KTV sing" << endl;
}
——————————————————————————————————————————————————————
主函数
#include <iostream>
#include "string"
#include<Windows.h>
#include <fstream>
#include  <sstream>
#include  "Father.h"
#include  "Son.h"
using namespace std;
void party(Father** men, int n)
{
	for (int i = 0; i < n; i++)
	{
		men[i]->play();
	}
}
int main(void) {
	Father father;
	Son son1, son2;
	//父类指针可以指向子类对象
	cout <<"----- Father* men[] = { &father, &son1, &son2 }; -----" << endl;
	Father* men[] = { &father, &son1, &son2 };
	party(men, sizeof(men)/ sizeof(men[0]));
	//这段函数的本意是想调用儿子的paly函数,但是实际上调用的是父类的
	cout << "----- Father* p -----" << endl;
	Father* p;
	p = &son1;
	p->play();
	return 0;
}
2. 虚函数的原理,用于实现多态

(1). 虚函数表

 
#include <iostream>
#include "string"
#include<Windows.h>
#include <fstream>
#include  <sstream>
using namespace std;
class Father
{
public:
	virtual void func1() { cout << "Father::func1" << endl; };
	virtual void func2() { cout << "Father::func2" << endl; };
	virtual void func3() { cout << "Father::func3" << endl; };
	void func4() { cout << "非虚函数,Father::func4" << endl; };
	int x = 200;
	int y = 300;
	static int z;
};
int Father::z = 0;
typedef void(*func_t)(void);//将数据类型转换成函数指针, 返回类型是void,输入参数也是void,也就是没有输入参数
int main(void) {
	Father father;
	//对象里面只装了虚函数表指针,指针为4字节,以及成员函数x,y 4字节+4字节,所以共12个字节
	cout << "sizeof(father)= "<< sizeof(father) << endl;  //输出值为12
	cout << "对象地址:" << (int*)&father << endl; //(int*)会按照16进制进行打印
	//(&father)表示取对象地址,将地址转成int型的指针(int*)(&father),然后再次取指针里面的值*(int*)(&father)
    //此时取到的是一个整数,再次将值转换成int*,因为左侧定义的是int* vptr,(int*)是强制类型转换
	int* vptr = (int*)*(int*)(&father);  //此处成功取到对象所指向的虚函数表的指针
	//*(vptr + 0 )表示取到了第一个虚函数的指针,虚函数表里面存的本身就是指针,
	//函数本身就是一个地址,因此强制类型转换,将取出来的指针转换成函数,就可以根据函数指针调用函数了
	cout << "调用第1个虚函数:"; ((func_t)*(vptr + 0 ))();
	cout << "调用第2个虚函数:"; ((func_t)*(vptr + 1))();
	cout << "调用第3个虚函数:"; ((func_t)*(vptr +2))();
	cout << " ********************" << endl;
	cout << "对象里面第1个数据成员的地址(方式1打印): " << &father.x << endl;
	//为什么加4,因为当前类是有两个成员变量,那么对象内首先存储的就是虚函数表指针,然后是整型变量x,然后是整型变量y
	//指针是4个字节,整型变量也是4个字节,&father默认是指向对象的第一个数据,也就是函数表指针,+4就指向了下一个整型数据
	cout << "对象里面第1个数据成员的地址(方式2打印): " << hex << (int)&father + 4<< endl;
	cout << "第1个数据成员的值(方式1打印): " << dec << father.x << endl;
	cout << "第1个数据成员的值(方式2打印): " << *(int*)((int)&father + 4) << endl;
	
	cout << " ********************" << endl;
	cout << "对象里面第2个数据成员的地址(方式1打印): " << &father.y << endl;
	cout << "对象里面第2个数据成员的地址(方式2打印): " << hex << (int)&father + 8  << endl;  //hex表示转为16进制表示
	cout << "第2个数据成员的值(方式1打印): " << dec << father.y << endl;
	cout << "第2个数据成员的值(方式2打印): " << *(int*)((int)&father + 8) << endl;
	return 0;
}
(2).子类的虚函数表
- 父类中有3个函数,子类定义了一个父类的同名函数和一个父类中没有的函数,子类的同名函数要实现自己的功能,观察此时虚函数表的变化。
  
  
#include <iostream>
#include "string"
#include<Windows.h>
#include <fstream>
#include  <sstream>
using namespace std;
class Father
{
public:
	virtual void func1() { cout << "Father::func1" << endl; };
	virtual void func2() { cout << "Father::func2" << endl; };
	virtual void func3() { cout << "Father::func3" << endl; };
	void func4() { cout << "非虚函数,Father::func4" << endl; };
	int x = 200;
	int y = 300;
	static int z;
};
int Father::z = 0;
class Son :public Father {
public:
	virtual void func1() { cout << "Son::func1" << endl; };
	virtual void func5() { cout << "Son::func5" << endl; };
};
typedef void(*func_t)(void);//将数据类型转换成函数指针, 返回类型是void,输入参数也是void,也就是没有输入参数
int main(void) {
	Son son;
	cout << "Son 对象地址:" << (int*)&son;//加 (int*)只是让打印按照指针格式打,不会改变值
	int* vptr = (int*)*(int*)(&son);
	cout << "虚函数表指针:vptr -- " << vptr << endl;
	for (int i = 0; i < 4; i++)
	{
		cout << "调用第" << i + 1 << "个虚函数" ;
		((func_t) * (vptr + i))();
	}
	for (int i = 0; i < 2; i++)
	{
		cout << *(int*)((int)&son +4 + i*4) << endl;
		
	}
	cout << "size of son = " << sizeof(son) << endl;
	return 0;
}
(3)final关键字,用来修饰类,让该类不能被继承
几种用法
- 如果用于类定义时做修饰,那么这个类将不能被继承,如下列定义的类不允许被继承
class Phone8848 final {};
- 用于类在继承上一个类的过程中做修饰,那么当前类将不允许被继承,XiaoMi3 类之后不允许被继承
class XiaoMi {};
class XiaoMi2: public XiaoMi {};
class XiaoMi3 final: public XiaoMi2{};
- 如果用于修饰虚函数(不允许用来修饰其他函数),子类可以继承父类的该函数,但是子类不允许对该函数进行修改
class XiaoMi{
	virtual void milioa() final;
};
class XiaoMi2: XiaoMi {
	void milioa();
};
(4) override关键字,只能用于虚函数

- 只需要在函数声明的时候用override,函数实现的时候不需要加这个关键字
class XiaoMi{
	virtual void milioa();
};
class XiaoMi2: XiaoMi {
	void milioa() override;  //提示程序员当前函数重写了父类的方法,实际上没有什么用
};
//函数实现
void XiaoMi2::milioa()
{
}
(5) 多态使用过程中,子类的析构函数不调用,导致内存泄露问题
- 以下案例,打印如下结果,有可能提示内存泄漏,有可能根本不提示,但是实际上有内存泄漏,因为没有delete指针
 case 3情况,使用了多态,用父类指针指向子类对象,但是释放内存的时候,不调用子类的析构函数,导致内存泄漏
- 解决方案是:在父类的析构函数前面加关键字virtual,把Father类的析构函数定义为virtual函数时,如果对father类的指针使用delete操作时,就会对指针使用动态析构,所谓动态析构:指的时如果father指针指向的子类对象,就会先调用子类的析构函数,再调用自己析构函数释放内存
- 在实际开发过程中,如果某一个类作为基类,推荐是对该类的析构函数都加上virtual关键字

#include <iostream>
#include "string"
#include<Windows.h>
#include <fstream>
#include  <sstream>
using namespace std;
class Father {
public:
	Father(const char* addr = "china") {
		cout << "执行了Father构造函数" << endl;
		int len = strlen(addr) + 1;
		this->addr = new char[len];
		strcpy_s(this->addr, len, addr);
	};
	
	//把Father类的析构函数定义为virtual函数时,如果对father类的指针使用delete操作时,就会对指针使用动态析构
	//所谓动态析构:指的时如果father指针指向的子类对象,就会先调用子类的析构函数,再调用自己析构函数释放内存
	//virtual ~Father() {
	~Father() {
		cout << "执行了Father析构函数" << endl;
		if (addr)
		{
			delete addr;
			addr = NULL;
		}
	};
private:
	char* addr;
};
class Son :public Father {
public:
	Son(const char* game = "吃鸡",const char* addr = "china"):Father(addr) {
		cout << "执行了Son构造函数" << endl;
		int len = strlen(game) + 1;
		this->game = new char[len];
		strcpy_s(this->game, len, game);
	};
	~Son() {
		cout << "执行了Son析构函数" << endl;
		if (game)
		{
			delete game;
			game = NULL;
		}
	};
private:
	char* game;
};
int main(void) {
	cout << "------case 1 --------" << endl;
	Father* father = new Father;
	delete father;
	cout << "------case 2 --------" << endl;
	Son* son = new Son;
	delete son;
	cout << "------case 3 --------" << endl;
	father = new Son;
	delete father;
	return 0;
}
3. 纯虚函数定义,什么时候用到纯虚函数
- 一旦类里面使用了纯虚函数,那么这个类就是抽象类,抽象类不能用来具体化对象,抽象类的目的是用来做基类,给其他的类做继承
- 子类继承抽象类之后,如果子类没有对父类所定于的纯虚函数做实现,那么子类也是抽象类,也不能用于具体化对象,但是如果子类对纯虚函数做实现,那么子类将不再是抽象类

class Shape{
public:
	Shape(const string& color = "White") { this->color = color; };
	//把当前函数定义为纯虚函数
	virtual float area() = 0;
	string getColor() { return color; };
private:
	string color;
};
class Circle :public Shape {
public:
	Circle(float radius = 0, const string& color = "White"):Shape(color), r(radius) {};
	float area() { return 3.14 * r * r; }
private:
	float r;
};
int main(void) {
	Circle c1(10);
	cout << c1.area() << endl;
	return 0;
}


















![三六零[601360]行情数据接口](https://i-blog.csdnimg.cn/direct/4ec342674adf4982b463a8427ab9008e.png)
