本文介绍: 在C++中,多态通过基类的指针或引用来触发。当通过基类指针或引用调用虚函数时,程序会在运行时确定实际对象的类型,并调用相应的函数。这种动态绑定的决策是通过虚函数表(vtable)来实现的。

目录

多态概念

多态定义

多态的触发机制

虚函数

虚函数表

虚析构函

虚析构函数声明

虚析构函数的作用

纯虚函数

纯虚函数的声明

纯虚函数的作用

抽象类

多态原理

虚函数表 & 虚函数指针

继承机制下的虚函数表

动态绑定


  • 多态概念

    • 狗狗发出的声音为 -> 旺旺
    • 猫猫发出的声音为 -> 喵喵
    • 猫猫狗狗我们均可以理解为是动物类,但是其具体对象做同一功能有着不同的表现
  • 多态定义

    • 多态的触发机制

      • 在C++中,多态通过基类的指针或引用来触发。
      • 当通过基类指针或引用调用虚函数时,程序会在运行时确定实际对象的类型,并调用相应的函数。
      • 这种动态绑定的决策是通过虚函数表(vtable)来实现的。
    • 虚函数

      • 虚函数是在基类中声明的带有virtual​​​关键字的成员函数。
      • 虚函数通过动态绑定来实现多态,它可以在派生类中被重写。
      • 当通过基类的指针或引用调用虚函数时,实际调用的是指向或引用的对象的类型的版本。
    • 代码示例

      #include <iostream>
      
      class Animal {
      public:
          virtual void MakeSound() {
              std::cout << "Animal makes a sound." << std::endl;
          }
      };
      
      class Dog : public Animal {
      public:
          void MakeSound() override {
              std::cout << "Dog barks." << std::endl;
          }
      };
      
      class Cat : public Animal {
      public:
          void MakeSound() override {
              std::cout << "Cat meows." << std::endl;
          }
      };
      
      int main() {
          Animal* animalPtr;
          Dog dog;
          Cat cat;
      
          animalPtr = &dog;
          animalPtr->MakeSound();  // 输出: Dog barks.
      
          animalPtr = &cat;
          animalPtr->MakeSound();  // 输出: Cat meows.
      
          return 0;
      }
      
  • 虚函数表

    • 虚函数表是用于实现多态的关键机制之一。

    • 每个包含虚函数的类都有一个虚函数表,其中存储了虚函数的地址。

    • 每个对象都有一个指向其类的虚函数表的指针(通常称为虚函数指针或vptr)。

    • 当调用虚函数时,程序会根据对象的虚函数指针找到相应的虚函数表,并使用表中的地址调用正确的函数。

  • 虚析构函

    • 如果一个基类的析构函数是虚函数,那么派生类的析构函数也会自动成为虚函数。

    • 虚析构函数声明

      • 虚析构函数是在基类中声明的带有virtual​​​​​关键字的析构函数。
      • ​virtual ~ClassName();​​​​​
    • 虚析构函数的作用

      • 当通过基类指针删除派生类对象时,如果基类的析构函数是虚函数,则会根据实际对象的类型调用相应的析构函数。
      • 这样可以确保正确释放派生类对象所占用的资源,避免内存泄漏。
      • 虚析构函数只需要在基类中声明,派生类的析构函数会自动成为虚函数。
      • 虚析构函数应该是公有的(public),以便在派生类中可以正确访问和重写。
    • 代码示例

      • 假设我们有一个图形库,其中包含多个图形类,如矩形(Rectangle​​)、圆形(Circle​​)。我们希望能够存储这些图形对象,并能够对它们执行各种操作,比如计算总面积、打印每个图形的属性等。
      • #include <iostream>
        #include <vector>
        
        using namespace std;
        
        class Shape {
        public:
            virtual double getArea() const = 0;
            virtual void printInfo() const = 0;
            virtual ~Shape() {}
        };
        
        class Rectangle : public Shape {
        private:
            double length;
            double width;
        
        public:
            Rectangle(double length, double width) : length(length), width(width) {}
        
            double getArea() const override {
                return length * width;
            }
        
            void printInfo() const override {
                cout << "矩形,长:" << length << ",宽:" << width << endl;
            }
        
            ~Rectangle() {
                cout << "销毁矩形对象" << endl;
            }
        };
        
        class Circle : public Shape {
        private:
            double radius;
        
        public:
            Circle(double radius) : radius(radius) {}
        
            double getArea() const override {
                return 3.14159 * radius * radius;
            }
        
            void printInfo() const override {
                cout << "圆形,半径:" << radius << endl;
            }
        
            ~Circle() {
                cout << "销毁圆形对象" << endl;
            }
        };
        
        int main() {
            vector<Shape*> shapes;
            shapes.push_back(new Rectangle(5.0, 3.0));
            shapes.push_back(new Circle(4.0));
        
            double totalArea = 0.0;
            for (const auto& shape : shapes) {
                shape->printInfo();
                totalArea += shape->getArea();
            }
        
            cout << "总面积:" << totalArea << endl;
        
            for (const auto& shape : shapes) {
                delete shape;
            }
        
            return 0;
        }
        
  • 纯虚函数

    • 纯虚函数的声明

      • 纯虚函数是通过在基类中声明一个没有实际实现的虚函数来定义的。
      • ​virtual ReturnType functionName() = 0;​​
    • 纯虚函数的作用

      • 纯虚函数为基类提供接口,要求派生类实现该函数。
      • 派生类必须提供对纯虚函数的定义,以便成为具体类。
      • 纯虚函数使得基类成为抽象类,无法实例化对象。
    • 抽象类

      • 抽象类包含至少一个纯虚函数。
      • 抽象类无法实例化对象,只能用作基类。
      • 抽象类可以包含非纯虚函数,这些函数可以有实际的实现。
      • 抽象类定义了一组接口和基本行为,要求派生类实现纯虚函数。
    • 代码示例

      #include <iostream>
      
      class AbstractClass {
      public:
          virtual void PureVirtualFunction() = 0;  // 纯虚函数
      
          void NonPureVirtualFunction() {
              std::cout << "Non-pure virtual function" << std::endl;
          }
      };
      
      class ConcreteClass : public AbstractClass {
      public:
          void PureVirtualFunction() override {
              std::cout << "Pure virtual function implementation" << std::endl;
          }
      };
      
      int main() {
          // AbstractClass abstractObj;  // 错误,无法实例化抽象类对象
      
          ConcreteClass concreteObj;
          concreteObj.PureVirtualFunction();
          concreteObj.NonPureVirtualFunction();
      
          AbstractClass* abstractPtr;
          abstractPtr = &concreteObj;
          abstractPtr->PureVirtualFunction();
          abstractPtr->NonPureVirtualFunction();
      
          return 0;
      }
      
    • 抽象类不能实例化对象,只能用作基类。
    • 派生类必须实现纯虚函数才能成为具体类。
    • 如果派生类没有实现纯虚函数,它仍然被视为抽象类,无法实例化对象。
    • 抽象类可以包含非纯虚函数,但纯虚函数必须在派生类中实现。
    • 纯虚函数可以具有实现,但通常没有实际实现,只是提供一个接口。
  • 多态原理

    • 虚函数表 & 虚函数指针

      • #include <iostream>
        
        class Base
        {
        public:
        	int a = 1;
        
        	virtual void Fun1()
        	{
        
        	}
        };
        
        class Son : public Base
        {
        public:
        	int a = 1;
        
        	virtual void Fun1()
        	{
        
        	}
        };
        
        
        int main()
        {
        	Base b;
        	Son s;
        
        	return 0;
        }
        
      • 定义一个空的类,写一个虚函数,观察期内存大小?

        • 类中仅有一个虚函数时类的内存大小为4Byte

          • 虚函数指针 – virtual function ptr​​(类对象前4Byte)

            • ​​
          • ​virtual function ptr​​ – 虚函数表

            • ​​
          • FunAddr

            • ​​
          • 内存布局

            • ​​
    • 继承机制下的虚函数表

      • #include <iostream>
        #include <Windows.h>
        
        class Base1
        {
        public:
        	virtual void Fun1(){}
        };
        
        class Base2
        {
        public:
        	virtual void Fun2() {}
        };
        
        class Son : public Base1, public Base2
        {
        public:
        	virtual void Fun1() {}
        	virtual void Fun2() {}
        };
        
        int main()
        {
        	//虚函数表
        	Son obj;
        	std::cout << std::hex << *(PDWORD)((PCHAR)(&obj) + 0) << std::endl;
        	std::cout << std::hex << *(PDWORD)((PCHAR)(&obj) + 4) << std::endl;
        
        	return 0;
        }
        
      • ​​
      • ​​
      • ​​
      • ​​
      • ​​
      • ​​
      • ​​
    • 动态绑定

      • 示例1

        • 代码

          #include <iostream>
          
          class Animal {
          public:
              virtual void MakeSound() {
                  std::cout << "Animal makes a sound." << std::endl;
              }
          };
          
          class Dog : public Animal {
          public:
              void MakeSound() override {
                  std::cout << "Dog barks." << std::endl;
              }
          };
          
          class Cat : public Animal {
          public:
              void MakeSound() override {
                  std::cout << "Cat meows." << std::endl;
              }
          };
          
          int main() {
              Animal* animalPtr;
              Dog dog;
              Cat cat;
          
              animalPtr = &dog;
              animalPtr->MakeSound();  // 输出: Dog barks.
          
              animalPtr = &cat;
              animalPtr->MakeSound();  // 输出: Cat meows.
          
              return 0;
          }
          
          
        • 图解

          • 对象地址

          • 获取对象地址

          • 指向函数

      • 示例2

        • 代码

          #include <iostream>
          #include <Windows.h>
          
          class Base1
          {
          public:
          	virtual void Fun1() {}
          };
          
          class Base2
          {
          public:
          	virtual void Fun2() {}
          };
          
          class Son : public Base1, public Base2
          {
          public:
          	virtual void Fun1() {}
          	virtual void Fun2() {}
          };
          
          int main()
          {
          	Base1* pBase1;
          	Base2* pBase2;
          	Son obj;
          
          	pBase1 = &obj;
          	pBase2 = &obj;
          
          	pBase1->Fun1();
          	pBase2->Fun2();
          
          	return 0;
          }
          
        • 图解

          • Base1

          • Base2

          • pBase1 -> obj.Addr + 0 = vfptr(1)

            pBase2 -> obj.Addr + 4 = vfptr(2)

原文地址:https://blog.csdn.net/2301_80612536/article/details/135960321

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。

如若转载,请注明出处:http://www.7code.cn/show_63875.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注