C++:多态究竟是什么?为何能成为面向对象的重要手段之一?

2024-02-26 1711阅读

温馨提示:这篇文章已超过399天没有更新,请注意相关的内容是否还可用!

C++:多态究竟是什么?为何能成为面向对象的重要手段之一?

  • 前言
  • 一、多态的概念
  • 二、多态的定义及实现
    • 2.1 多态的构成条件
    • 2. 2 虚函数
    • 2.3 虚函数的重写
      • 2.3.1 虚函数重写的例外1:协变(基类与派生类虚函数返回值类型不同)
      • 2.3.2 虚函数重写的例外2:析构函数的重写(基类与派生类析构函数的名字不同)
      • 2.4 C++11 override 和 final
        • 2.4.1 final
        • 2.4.2 override
        • 2.5 重载、覆盖(重写)、隐藏(重定义)的对比
        • 三、抽象类
          • 3.1 概念
          • 3.2 接口继承和实现继承
          • 四、虚函数及虚函数表
            • 4.1 虚函数表
            • 4.2 虚函数重写覆盖虚表
            • 4.3 虚函数存在哪的?虚表存在哪的?
            • 五、多态原理
              • 5.1 多态调用过程及原理
              • 5.2 多态运行时确定?
              • 5.3 动态绑定与静态绑定
              • 六、单继承和多继承关系的虚函数表
                • 6.1 单继承中的虚函数表
                • 6.2 多继承中,子类新增虚函数存入那个虚表?
                • 6.3 总结
                • 七、菱形继承、菱形虚拟继承

                  前言

                  本篇博客基于VS2019X86环境下,后续关于多态原理相关验证都是基于vsX86环境,而虚表本质上是一个虚函数指针数组,在X86环境下VS编译器会在数组最后放一个unllptr!!

                  一、多态的概念

                  多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。

                  比如买票这个行为,当普通人买票时,是全价买票;学生买票时,是半价买票;军人买票时是优先买票。

                  二、多态的定义及实现

                  2.1 多态的构成条件

                  多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。

                  但在继承中要构成多态还有两个条件:(后续会具体介绍其细节)

                  1. 必须通过基类的指针或者引用调用虚函数。
                  2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。

                  比如Student继承了Person。Person对象买票全价,Student对象买票半价:

                  class Person
                  {
                  public:
                  	virtual void BuyTicket()//被virtual修饰的类成员函数称为虚函数
                  	{
                  		cout 
                  public:
                  	virtual void BuyTicket()//对基类虚函数重写
                  	{
                  		cout 
                  	people.BuyTicket();
                  }
                  int main()
                  {
                  	Person p;
                  	Func(p);//涉及赋值兼容规则,后续会介绍。
                  	Student s;
                  	Func(s);
                  	return 0;
                  }
                  
                  public:
                  	//BuyTicket()为虚函数
                  	virtual void BuyTicket() { cout 
                  public:
                  	virtual void BuyTicket() { cout 
                  public:
                  	virtual void BuyTicket() { cout 
                  	p.BuyTicket();
                  }
                  int main()
                  {
                  	Person p;
                  	Student s;
                  	Func(p);
                  	Func(s);
                  	return 0;
                  }
                  };
                  class B : public A {};
                  class Person 
                  {
                  public:
                  	virtual A* f() { return new A; }
                  };
                  class Student : public Person 
                  {
                  public:
                  	virtual B* f() { return new B; }
                  };
                  
                  public:
                  	virtual ~Person() { cout 
                  public:
                  	virtual ~Student() { cout 
                  	Person* p1 = new Person;
                  	Person* p2 = new Student;
                  	
                  	delete p1;
                  	// delete p1编译器处理为: p1-destrutor() + operator delete(p1)
                  	delete p2;
                  	// delete p2编译器处理为: p2-destrutor() + operator delete(p2)
                  	return 0;
                  }
                  
                  public:
                  	virtual void Drive() final {}
                  };
                  class Benz :public Car
                  {
                  public:
                  	virtual void Drive() { cout 
                  public:
                  	virtual void Drive() {}
                  };
                  class Benz :public Car {
                  public:
                  	virtual void Drive() override { cout 
                  public:
                  	virtual void Drive() = 0;//纯虚函数
                  };
                  class Benz :public Car
                  {
                  public:
                  	virtual void Drive()
                  	{
                  		cout 
                  public:
                  	virtual void Drive()
                  	{
                  		cout 
                  	Car* pBenz = new Benz;
                  	pBenz-Drive();
                  	Car* pBMW = new BMW;
                  	pBMW-Drive();
                  }
                  
                  public:
                  	virtual void Func1()
                  	{
                  		cout 
                  	Base b1;
                  	cout 
                  public:
                  	virtual void Func1()
                  	{
                  		cout 
                  		cout 
                  		cout 
                  public:
                  	virtual void Func1()
                  	{
                  		cout 
                  	Base b;
                  	Derive d;
                  	return 0;
                  }
                  
                  public:
                  	virtual void func1() { cout  cout 
                  	cout 
                  	Base b1;
                  	int a;
                  	int* a2 = new int;
                  	//const int a3 = 1;
                  	const char* s = "hello world";
                  	static int d4 = 0;
                  	printf("栈:%p\n", &a);
                  	printf("堆:%p\n", a2);
                  	printf("静态区:%p\n", &d4);
                  	//printf("代码段:%p\n", &a3);
                  	printf("代码段:%p\n", s);
                  	printf("虚表:%p\n", *(int*)(&b1));
                  	//在类域中的函数地址比较特殊,要加&
                  	printf("虚函数地址:%p\n", &Base::func1);
                  	printf("普通函数地址:%p\n", func);
                  	return 0;
                  }
                  
                  public:
                  	virtual void BuyTicket() { cout 
                  public:
                  	virtual void BuyTicket() { cout 
                  	p.BuyTicket();
                  }
                  int main()
                  {
                  	Person Mike;
                  	Func(Mike);
                  	Student Johnson;
                  	Func(Johnson);
                  	return 0;
                  }
                  
                   p-BuyTicket();
                  }
                  int main()
                  {
                   Person mike;
                   Func(&mike);
                   mike.BuyTicket();
                      
                   return 0;
                  }
                  // 以下汇编代码中跟你这个问题不相关的都被去掉了
                  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  
                  00头1940EC  cmp         esi,esp  
                  }
                  int main()
                  {
                  ... 
                  // 首先BuyTicket虽然是虚函数,但是mike是对象,不满足多态的条件,所以这里是普通函数的调
                  用转换成地址时,是在编译时已经从符号表确认了函数的地址,直接call 地址
                   mike.BuyTicket();
                  00195182  lea         ecx,[mike]
                  00195185  call        Person::BuyTicket (01914F6h)  
                  ... 
                  }
                  
                  public:
                  	virtual void func1() { cout  cout 
                  public:
                  	virtual void func1() { cout  cout  cout  cout 
                  public:
                  	virtual void func3() { cout 
                  	Base b;
                  	Derive d;
                  	X x;
                  	return 0;
                  }
                  
                  	//由于VSX86环境下,编译器会在虚表结尾放一个nullptr,所以此处结束条件为a[i]!=0
                  	for (int i = 0; a[i] != 0; i++)
                  	{
                  		printf("[%d]-%p ", i, a[i]);
                  		//调用a[i]中存储的函数
                  		VFUNC f = a[i];
                  		f();
                  		//(*f)()
                  		cout 
                  	Base b;
                  // 思路:取出b、d对象的头4bytes,就是虚表的指针,前面我们说了虚函数表本质是一个存虚函数
                  //      指针的指针数组,这个数组最后面放了一个nullptr
                  // 1.先取b的地址,强转成一个int*的指针
                  // 2.再解引用取值,就取到了b对象头4bytes的值,这个值就是指向虚表的指针
                  // 3.再强转成VFPTR*,因为虚表就是一个存VFPTR类型(虚函数指针类型)的数组。
                  // 4.虚表指针传递给PrintVTable进行打印虚表
                  // 5.需要说明的是这个打印虚表的代码经常会崩溃,因为编译器有时对虚表的处理不干净,虚表最
                  //后面没有放nullptr,导致越界,这是编译器的问题。我们只需要点目录栏的-生成-清理解决方案,再编译就好了。
                  	printVFT((VFUNC*)(*((int*)(&b))));
                  	printf("----------------------------------\n");
                  	Derive d;
                  	printVFT((VFUNC*)(*(int*)(&d)));
                  	printf("----------------------------------\n");
                  	X x;
                  	printVFT((VFUNC*)(*(int*)(&x)));
                  	return 0;
                  }
                  
                  public:
                  	virtual void func1() { cout  cout 
                  public:
                  	virtual void func1() { cout  cout 
                  public:
                  	virtual void func1() 
                  	{ 
                  		cout  cout 
                  	for (int i = 0; a[i] != 0; i++)
                  	{
                  		printf("[%d]-%p ", i, a[i]);
                  		VFUNC f = a[i];
                  		f();
                  	}
                  	cout 
                  	Derive d;
                  	printVFT((VFUNC*)(*(int*)&d));
                  	printf("-----------------------------\n");
                  	//由于d对象中存有两张虚表,要打印第2张虚表,需要将&d偏移指向Base2开头,有如下几种方式
                  	//printvft((vfunc*)(*(int*)((char*)&d+sizeof(base1))));
                  	Base2* ptr = &d;
                  	printVFT((VFUNC*)(*(int*)ptr));
                  	return 0;
                  }
                  
VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]