C++笔记 day32 多态 虚函数 类型转换 重载

136 阅读4分钟

静态联编动态联编

多态性提供接口与具体实现之间的另一层隔离,从而将“what”和“how”分离开来。多态性改善了代码的可读性和组织性,项目不仅在最初创建时期可以扩展,而且当项目在需要有新的功能时也能扩展。

静态多态和动态多态

区别是函数地址的早晚绑定,早绑定(静态联编),晚绑定(动态联编)。

如果函数的调用在编译阶段就可以确定函数的调用地址,并产生地址就是早绑定的。

静态多态:函数重载,运算符重载

动态多态:

先有继承关系

父类中有虚函数,子类重写父类中的虚函数

父类的指针或引用  指向子类的对象

静态多态在编译阶段绑定地址,地址早绑定,静态联编

动态多次在运行阶段绑定地址,地址晚绑定,动态联编

多态原理

当父类写了虚函数后,类内部结构发生改变,多了一个vfptr

vfptr 虚函数表指针  ---- > vftable 虚函数表

虚函数表内部记录着 虚函数的入口地址

当父类指针或引用指向子类对象,发生多态,调用是时候从虚函数中找函数入口地址

虚函数 关键字  virtual

image.png

可以利用指针的偏移调用 函数

image.png

((void(*)()) (*(int *)*(int *)animal)) ();

有参的时候,因为调用惯例不同,出栈的时候会崩掉,因为一个认为是主调函数释放,一个认为被调函数释放,可以采用typedef的方式解决。

typedef void( __stdcall *FUNPOINT)(int);

(FUNPOINT (*((int*)*(int*)animal + 1)))(10);

image.png

image.png

示例

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Animal 
{
public:
	virtual void speak() //变成虚函数
	{
		cout << "动物在说话" << endl;
	}
};

class Cat :public Animal
{
public :
	void speak() 
	{
		cout << "小猫在说话" << endl;
	}
};

class Dog :public Animal
{
public:
	void speak()
	{
		cout << "小狗在说话" << endl;
	}
};

//动态多态产生条件:
//先有继承关系
//父类中有虚函数,子类中重写父类中的虚函数
//父类的指针或引用 指向子类的对象

//对于有父子关系的两个类,指针或者引用 是可以直接转换的
void doSpeak(Animal& animal) 
{
	//如果地址早就绑定好了,地址早绑定,属于静态联编
	//如果想调用小猫说话,这个时候函数的地址就不能早就绑定好了,而是在运行阶段再去绑定函数的地址,属于地址晚绑定,也就是动态联编
	animal.speak(); 
}

void test01() 
{
	Cat cat;
	doSpeak(cat);

	Dog dog;
	doSpeak(dog);

	Animal ani;
	doSpeak(ani);
}

void test02() 
{
	cout << "sizeof Animal = " << sizeof(Animal) << endl;

}

int main()
{
	test02();


	system("pause");
	return EXIT_SUCCESS;
}

多态案例 - 计算器案例

设计抽象计算器类,分别实现加减乘计算,继承于抽象计算器类,重写虚函数

利用多态可以调用不同计算器

多态的好处

  1. 代码可读性强
  2. 组织结构清晰
  3. 扩展性强

开闭原则: 对扩展进行开放  对修改进行关闭

示例

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
#include <string>

//无多态情况
//class calculator 
//{
//public:
//	int getResult(string oper) 
//	{
//		if (oper == "+") 
//		{
//			return m_A + m_B;
//		}
//		if (oper == "-")
//		{
//			return m_A - m_B;
//		}
//		if (oper == "*")
//		{
//			return m_A * m_B;
//		}
//	}
//	int m_A;
//	int m_B;
//};

//设计原则:开闭原则
//对扩展进行开放,对修改进行关闭

//利用多态实现计算器
class AbstractCalculator 
{
public:
	virtual int getResult() 
	{
		return 0;
	}

	int m_A;
	int m_B;
};

//加法计算器
class AddCalculator :public AbstractCalculator 
{
public:
	int getResult() 
	{
		return m_A + m_B;
	}
};
//减法计算器
class SubCalculator :public AbstractCalculator
{
public:
	int getResult()
	{
		return m_A - m_B;
	}
};
//乘法计算器
class MulCalculator :public AbstractCalculator
{
public:
	int getResult()
	{
		return m_A * m_B;
	}
};
//除法计算器


void test01() 
{
	//无多态情况
	//calculator c;
	//c.m_A = 10;
	//c.m_B = 30;
	//cout << c.getResult("+") << endl;

	//多态,可以改善代码的可读性和可扩展性
	AbstractCalculator * calculator = new AddCalculator;
	calculator->m_A = 20;
	calculator->m_B = 30;
	cout << calculator->getResult() << endl;
	delete calculator;
	
	calculator = new SubCalculator;
	calculator->m_A = 200;
	calculator->m_B = 100;
	cout << calculator->getResult() << endl;

}

int main()
{

	test01();

	system("pause");
	return EXIT_SUCCESS;
}

纯虚函数和抽象类

语法: virtual int getResult() = 0;

如果一个类中包含了纯虚函数,那么这个类就无法实例化对象了,这个类通常我们称为 抽象类

抽象类的子类 必须要重写 父类中的纯虚函数,否则也属于抽象类

虚析构和纯虚析构

虚析构语法:virtual ~Animal(){}

如果子类中有指向堆区的属性,那么要利用虚析构技术 在delete的时候 调用子类的析构函数

纯虚析构语法:

    virtual ~Animal() = 0;

    Animal::~Animal(){ .. }

纯虚析构 需要有声明 也需要有实现

如果一个类中 有了 纯虚析构函数,那么这个类也属于抽象类,无法实例化对象了

示例:

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Animal
{
public:
	Animal() 
	{
		cout << "Animal的构造函数调用" << endl;
	}
	virtual void speak() 
	{
		cout << "动物在说话" << endl;
	}
	//如果子类中有指向堆区的属性,那么要利用虚析构技术,在delete的时候,调用子类的析构函数
	//virtual ~Animal()
	//{
	//	cout << "Animal的析构函数调用" << endl;
	//}

	//纯虚析构 需要有声明 也需要有实现
	//如果一个类中有个纯虚析构函数,那么这个类也属于抽象类,无法实例化对象了
	virtual ~Animal() = 0;
};

Animal ::~Animal() 
{
	cout << "Animal 的析构函数调用" << endl;
}

class Cat :public Animal 
{
public:
	Cat(const char* name) 
	{
		cout << "Cat 的构造函数的调用" << endl;
		this->m_Name = new char[strlen(name) + 1];
		strcpy(this->m_Name, name);
	}
	virtual void speak() 
	{
		cout <<this->m_Name<< "小猫在说话" << endl;
	}
	~Cat() 
	{
		if (this->m_Name) 
		{
			cout << "Cat 析构函数调用" << endl;
			delete[] this->m_Name;
			this->m_Name = NULL;
		}
	}
	char* m_Name;
};

void test01() 
{
	Animal * animal = new Cat("Tom");
	animal->speak();
	delete animal;
}

int main()
{
	test01();


	system("pause");
	return EXIT_SUCCESS;
}

向上类型转换和向下类型转换

父转子:向下类型转换   不安全

子转父:向上类型转换   安全

如果发生多态,那么转换永远都是安全的

image.png

示例

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

class Animal
{
public:
	Animal() 
	{
		cout << "Animal的构造函数调用" << endl;
	}
	virtual void speak() 
	{
		cout << "动物在说话" << endl;
	}
	//如果子类中有指向堆区的属性,那么要利用虚析构技术,在delete的时候,调用子类的析构函数
	//virtual ~Animal()
	//{
	//	cout << "Animal的析构函数调用" << endl;
	//}

	//纯虚析构 需要有声明 也需要有实现
	//如果一个类中有个纯虚析构函数,那么这个类也属于抽象类,无法实例化对象了
	virtual ~Animal() = 0;
};

Animal ::~Animal() 
{
	cout << "Animal 的析构函数调用" << endl;
}

class Cat :public Animal 
{
public:
	Cat(const char* name) 
	{
		cout << "Cat 的构造函数的调用" << endl;
		this->m_Name = new char[strlen(name) + 1];
		strcpy(this->m_Name, name);
	}
	virtual void speak() 
	{
		cout <<this->m_Name<< "小猫在说话" << endl;
	}
	~Cat() 
	{
		if (this->m_Name) 
		{
			cout << "Cat 析构函数调用" << endl;
			delete[] this->m_Name;
			this->m_Name = NULL;
		}
	}
	char* m_Name;
};

void test01() 
{
	Animal * animal = new Cat("Tom");
	animal->speak();
	delete animal;
}

int main()
{
	test01();


	system("pause");
	return EXIT_SUCCESS;
}

重载、重写、重定义

重载

函数重载

同一个作用域下,函数名称相同,参数个数、顺序、类型不同

image.png

重写

子类重写父类中的虚函数,函数返回值、函数名、形参列表完全一致称为重写

image.png

重定义

子类重新定义父类中的同名成员函数,隐藏掉父类中同名成员函数,如果想调用加作用域

image.png

示例

image.png

多态案例  - 电脑组装案例

利用多态实现 电脑组装

image.png

#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;

//CPU基类
class CPU
{
public:
	virtual void calculate() = 0;
};
//显卡基类
class VideoCard
{
public:
	virtual void display() = 0;
};
//内存基类
class Memory 
{
public:
	virtual void storage() = 0;
};

//电脑类
class computer 
{
public:

	computer(CPU * cpu, VideoCard * vc,Memory * mem) 
	{
		cout << "电脑构造调用" << endl;
		this->m_Cpu = cpu;
		this->m_Vc = vc;
		this->m_Mem = mem;
	}

	void doWork() 
	{
		this->m_Cpu->calculate();
		this->m_Vc->display();
		this->m_Mem->storage();
	}
	~computer() 
	{
		cout << "电脑析构调用" << endl;
		if (this->m_Cpu) 
		{
			delete this->m_Cpu;
			this->m_Cpu = NULL;
		}
		if (this->m_Vc)
		{
			delete this->m_Vc;
			this->m_Vc = NULL;
		}
		if (this->m_Mem)
		{
			delete this->m_Mem;
			this->m_Mem = NULL;
		}
	}

	CPU* m_Cpu;
	VideoCard* m_Vc;
	Memory* m_Mem;
};

//intel 厂商
class intelCPU :public CPU 
{
public:
	void calculate() 
	{
		cout << "Intel cpu 计算了" << endl;
	}
};
class intelVideoCard :public VideoCard
{
public:
	void display()
	{
		cout << "Intel 显卡显示了" << endl;
	}
};
class intelMemory :public Memory
{
public:
	void storage()
	{
		cout << "Intel 内存存储了" << endl;
	}
};

//Lenovo厂商
class LenovoCPU :public CPU
{
public:
	void calculate()
	{
		cout << "Lenovo cpu 计算了" << endl;
	}
};
class LenovoVideoCard :public VideoCard
{
public:
	void display()
	{
		cout << "Lenovo 显卡显示了" << endl;
	}
};
class LenovoMemory :public Memory
{
public:
	void storage()
	{
		cout << "Lenovo内存存储了" << endl;
	}
};

void test01() 
{
	cout << "第一台电脑组成:" << endl;
	CPU* intelCpu = new intelCPU;
	VideoCard* lenovoVC = new LenovoVideoCard;
	Memory* lenovoMem = new LenovoMemory;

	computer c1(intelCpu, lenovoVC, lenovoMem);
	c1.doWork();


}

int main()
{
	test01();


	system("pause");
	return EXIT_SUCCESS;
}