C++-多态

111 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天点击查看活动详情

静态多态

函数重载 运算符重载 编译阶段确定函数地址

动态多态

派生类 虚函数实现运行时多态 运行阶段确定函数地址

动态多态满足关系

1,有继承关系 2.子类重写父类的虚函数 3.重写 需要函数返回值类型 函数名 参数列表 完全相同

动态多态使用

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

(虚函数调用)允许父类指针(引用)指向子类对象

父类引用指向子类对象: yang yan; animal ani& yan; 父类指针指向子类对象: animal *ani = new yang;

class animal
{
public:
    //如果在这里写virtual void speak()那么就能实现地址晚绑定 下面输出的就是yang is speaking
	void speak()
	{
		cout << "animal is speaking";
	}
};
class yang :public animal
{
public:
	void speak()
	{
		cout << "yang is spraking";
	}
};
//如果上面的animal类函数中没有写virtual 则这里是静态多态 地址在这里的编译阶段就已经确定是animal 无论是传他的什么子类对象都是animal
void dospeak(animal &ani) //允许animal& ani = yan;
{
	ani.speak();
}

int main()
{
	yang yan;
	dospeak(yan);//实际输出的是animal is speaking
}

虚函数多态实现原理

原理就是 基类指针指向派生类对象 他在执行函数时就不再是直接去代码区找 而是先在该类的虚函数表中找到该函数地址 再去代码区找 在这里插入图片描述 在这里插入图片描述 在这里插入图片描述 如果注释掉重写的猫说话函数 这里还是四个字节的原因是虽然没有重写speak对象 但是继承了animal的虚函数指针 所以也是四个字节 也就是如果类里面有虚函数 就会多出四个字节 这四个字节会存储一个地址 就是虚函数表的地址

虚函数表(vfptr)

是一个线性表 本质是一个动态数组 数组的元素是函数指针 指针指向该函数在代码区中的地址 派生类继承基类时会继承虚函数表中的元素 如果派生类继承了基类 但是并没有重写虚函数 那么虚函数表中继承的指针依旧指向基类虚函数在代码区的地址 当重写之后 就会在代码区新开辟一块地址存储新的函数内容 并且派生类中虚函数表指针指向它 在这里插入图片描述

获取虚函数表地址

	Father father;
	Son son;
	int* ptrAdd = (int *)&father;//强制转换的原因是我们只需要拿这个对象的前四个字节的地址 因为虚函数表的地址就在这
	printf("虚函数表地址:%p\n", *ptrAdd);
	int Add = *((int*)*ptrAdd);
	printf("虚函数表第一个元素地址:%p\n", Add);

在这里插入图片描述 获取虚函数地址 通过函数指针指向虚函数地址调用函数

class Father
{public:
	virtual void fun()
	{
		cout << "i am father" << endl;
	}
};
class Son :public Father
{
public:
	virtual void fun()
	{
		cout << "i am son" << endl;
	}
	void fun2()
	{
		cout << "son fun2" << endl;
	}
};
typedef void (*FUNC)();
int main()
{
	Father father;
	Son son;
	/*获取虚函数表的地址*/
	int* ptrAdd = (int *)&father;//强制转换的原因是我们只需要拿这个对象的前四个字节的地址 因为虚函数表的地址就在这
	printf("虚函数表地址:%p\n", *ptrAdd);
	/*获取虚函数表的第一个元素的地址 也就是存储的第一个方法的地址*/
	int Add = *((int*)*ptrAdd);
	printf("虚函数表第一个元素地址:%p\n", Add);
	/*创建函数指针 指向函数地址*/
	FUNC pFunc = (FUNC)Add;
	/*调用函数指针*/
	pFunc();
	return 0;
}

在这里插入图片描述

多态实现计算器实例

class AbstractCalculator
{
public:
	virtual int getResult()//方便子类重写
	{
		return 0;
	}
	int num1;
	int num2;
};
class AddCalculator :public AbstractCalculator
{
public:
	int getResult()
	{
		return num1 + num2;
	}
};
class SubCalculator :public AbstractCalculator
{
public:
	int getResult()
	{
		return num1 - num2;
	}
};
class MulCalculator :public AbstractCalculator
{
public:
	int getResult()
	{
		return num1 * num2;
	}
};
int main()
{
	AbstractCalculator* cal = new AddCalculator;
	cal->num1 = 100;
	cal->num2 = 200;
	cout << cal->num1 << "+" << cal->num2 << "=" << cal->getResult() << endl;
	delete cal;//一定要记得销毁堆内存
	cal = new SubCalculator;
	cal->num1 = 100;
	cal->num2 = 200;
	cout << cal->num1 << "-" << cal->num2 << "=" << cal->getResult() << endl;
	delete cal;//一定要记得销毁堆内存
	cal = new MulCalculator;//这里就体先了多态的作用 只需要创建一个对象 就行了
	cal->num1 = 100;
	cal->num2 = 200;
	cout << cal->num1 << "*" << cal->num2 << "=" << cal->getResult() << endl;
	delete cal;//一定要记得销毁堆内存
}