0
点赞
收藏
分享

微信扫一扫

【C++进阶学习】C++中的多态

Mhhao 2022-01-31 阅读 83
c++多态

【C++进阶学习】C++中的多态

零、前言

一、多态的概念和定义

  • 概念:
  • 示例:买票
  • 定义:
  • 多态构成条件:

  • 示图:

image-20220105190500103

二、虚函数

1、概念和定义

  • 虚函数语法:
  • 示例:
class Person {
public:
	virtual void BuyTicket() 
	{ 
		cout << "买票-全价" << endl; 
	}
};
  • 虚函数重写:
  • 示例:
class Person {
public:
	virtual void BuyTicket() 
	{ 
		cout << "买票-全价" << endl; 
	}
};
class Student : public Person {
public:
	virtual void BuyTicket()
	{ 
		cout << "买票-半价" << endl;
	}
};

void Func(Person& p)
{
	p.BuyTicket();
}
int main()
{
	Person ps;
	Student st;
	Func(ps);
	Func(st);
	return 0;
}
  • 结果:
image-20220105191849067

2、虚函数重写的特例

  1. 协变
  • 示例:
class A {};
class B : public A {};
class Person {
public:
	virtual A* f() 
	{ 
		cout << "virtual A* f()" << endl;
		return new A; 
	}
};
class Student : public Person {
public:
	virtual B* f() 
	{ 
		cout << "virtual B* f()" << endl;
		return new B; 
	}
};

void Func(Person& p)
{
	p.f();
}

int main()
{
	Person ps;
	Student st;
	Func(ps);
	Func(st);
	return 0;
}
  • 结果:
image-20220105192447963
  1. 派生类虚函数不加virtual
  • 原因:
  • 示例:
class Person {
public:
	virtual void BuyTicket()
	{
		cout << "买票-全价" << endl;
	}
};
class Student : public Person {
public:
	//派生类虚函数不加virtual也构成虚函数
	void BuyTicket() 
	{ 
		cout << "买票-半价" << endl; 
	}
};

void Func(Person& p)
{
	p.BuyTicket();
}

int main()
{
	Person ps;
	Student st;
	Func(ps);
	Func(st);
	return 0;
}
  • 结果:
image-20220105193036679
  • 注意:
  1. 析构函数的重写
  • 示例:
class Person {
public:
	virtual ~Person() 
	{ 
		cout << "~Person()" << endl; 
	}
};
class Student : public Person {
public:
	virtual ~Student() 
	{ 
		cout << "~Student()" << endl; 
	}
};
//只有派生类Student的析构函数重写了Person的析构函数
//当delete对象调用析构函数,才能构成多态,才能保证p1和p2指向的对象正确的调用析构函数
int main()
{
	Person* p1 = new Person;
	Person* p2 = new Student;
	delete p1;
	delete p2;
	return 0;
}
  • 结果:
image-20220105194347744

3、C++11 override 和 final

  • 引入:
  1. final
  • 示例:
class Car
{
public:
	virtual void Drive() final {}
};
class Benz :public Car
{
public:
	virtual void Drive() { cout << "Benz-舒适" << endl; }
};
  • 结果:
image-20220105200014783
  1. override
  • 示例:
class Car{
public:
virtual void Drive(){}
};
class Benz :public Car {
public:
virtual void Drive() override {cout << "Benz-舒适" << endl;}
};
  • 结果:
image-20220105202344834

4、重载/重写/重定义对比

  • 对比示图:
image-20220105202531629

三、抽象类

  • 概念:
  • 示例:
class Car
{
public:
	virtual void Drive() = 0;
};
class Benz :public Car
{
public:
	virtual void Drive()
	{
		cout << "Benz-舒适" << endl;
	}
};
class BMW :public Car
{
public:
	virtual void Drive()
	{
		cout << "BMW-操控" << endl;
	}
};
void Test()
{
	Car* pBenz = new Benz;
	pBenz->Drive();
	Car* pBMW = new BMW;
	pBMW->Drive();
}
  • 结果:
image-20220105203142217
  • 接口继承和实现继承:
  • 注意:

四、多态的原理

1、虚函数表

  • 例题:
// 这里常考一道笔试题:sizeof(Base)是多少?
class Base
{
public:
	virtual void Func1()
	{
		cout << "Func1()" << endl;
	}
private:
	int _b = 1;
};
  • 结果:
image-20220105205519902
  • 解释:
  • 注意:
  • 示例:
// 针对上面的代码我们做出以下改造
// 1.我们增加一个派生类Derive去继承Base
// 2.Derive中重写Func1
// 3.Base再增加一个虚函数Func2和一个普通函数Func3
class Base
{
public:
	virtual void Func1()
	{
		cout << "Base::Func1()" << endl;
	}
	virtual void Func2()
	{
		cout << "Base::Func2()" << endl;
	}
	void Func3()
	{
		cout << "Base::Func3()" << endl;
	}
private:
	int _b = 1;
};
class Derive : public Base
{
public:
	virtual void Func1()
	{
		cout << "Derive::Func1()" << endl;
	}
private:
	int _d = 2;
};
int main()
{
	Base b;
	Derive d;
	return 0;
}
  • 示图:
image-20220105205009639
  • 说明:
  • 派生类的虚表生成总结:
  • 注意:

2、多态的原理

  • 示例:
class Person {
public:
	virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:
	virtual void BuyTicket() { cout << "买票-半价" << endl; }
};
void Func(Person& p)
{
	p.BuyTicket();
}
int main()
{
	Person Mike;
	Func(Mike);
	Student Johnson;
	Func(Johnson);
	return 0;
}
  • 示图:
image-20220105205718838
  • 说明:
  • 示图:
image-20220105205855460
  • 汇编:
// 以下汇编代码中跟你这个问题不相关的都被去掉了
void Func(Person* p)
{
    ...
    p->BuyTicket();
    // p中存的是mike对象的指针,将p移动到eax中
    001940DE mov eax,dword ptr [p]
    // [eax]就是取eax值指向的内容,这里相当于把mike对象头4个字节(虚表指针)移动到了edx
    001940E1 mov edx,dword ptr [eax]
    // [edx]就是取edx值指向的内容,这里相当于把虚表中的头4字节存的虚函数指针移动到了eax
    00B823EE mov eax,dword ptr [edx]
    // call eax中存虚函数的指针。这里可以看出满足多态的调用,不是在编译时确定的,是运行起来以后到对象的中取找的
    001940EA call eax
    001940EC cmp esi,esp
}
int main()
{
    ...
    // 首先BuyTicket虽然是虚函数,但是mike是对象,不满足多态的条件,所以这里是普通函数的调用转换成地址时,是在编译时已经从符号表确认了函数的地址,直接call 地址
    mike.BuyTicket();
    00195182 lea ecx,[mike]
    00195185 call Person::BuyTicket (01914F6h)
    ...
}

3、动态绑定与静态绑定

  • 概念:

4、多继承虚函数表

  • 示例:
class Base1 {
public:
	virtual void func1() { cout << "Base1::func1" << endl; }
	virtual void func2() { cout << "Base1::func2" << endl; }
private:
	int b1;
};
class Base2 {
public:
	virtual void func1() { cout << "Base2::func1" << endl; }
	virtual void func2() { cout << "Base2::func2" << endl; }
private:
	int b2;
};
class Derive : public Base1, public Base2 {
public:
	virtual void func1() { cout << "Derive::func1" << endl; }
	virtual void func3() { cout << "Derive::func3" << endl; }
private:
	int d1;
};
typedef void(*VFPTR) ();//这种函数指针的强转是一种特例
void PrintVTable(VFPTR vTable[])
{
	cout << " 虚表地址>" << vTable << endl;
	for (int i = 0; vTable[i] != nullptr; ++i)
	{
		printf(" 第%d个虚函数地址 :0X%x,->", i, vTable[i]);
		VFPTR f = vTable[i];
		f();
	}
	cout << endl;
}
int main()
{
	Derive d;
	VFPTR* vTableb1 = (VFPTR*)(*(int*)&d);
    //取对象地址,(虚函数表指针一般存在对象前四个字节)要取前四个字节地址则用int*进行强转(只能指针类型强转才能改变指针的步长)
    //再进行解引用拿到虚函数表指针,然而这样子无法进行传参(类型不匹配),要强转为虚函数指针数组类型
	PrintVTable(vTableb1);
	VFPTR* vTableb2 = (VFPTR*)(*(int*)((char*)&d + sizeof(Base1)));//先转为char*步长便于越过Base1类去拿到Base2的地址
	PrintVTable(vTableb2);
	return 0;
}
  • 示图:
image-20220105210747465
  • 说明:
  • 为什么对于两个虚函数func1的覆盖地址会不一样?
  • 对于菱形继承/菱形虚拟继承:

五、继承和多态常见的面试问题

  1. 什么是多态?
  1. 什么是重载、重写(覆盖)、重定义(隐藏)?
  1. 多态的实现原理?
  1. inline函数可以是虚函数吗?
  1. 静态成员可以是虚函数吗?
  1. 构造函数可以是虚函数吗?
  1. 析构函数可以是虚函数吗?什么场景下析构函数是虚函数?
  1. 对象访问普通函数快还是虚函数更快?
  1. 虚函数表是在什么阶段生成的,存在哪的?
  1. C++菱形继承的问题?虚继承的原理?
  1. 什么是抽象类?抽象类的作用?
举报

相关推荐

【C++进阶】多态

C++中的多态

C++学习013多态

C++学习笔记——多态

C++多态

0 条评论