目录
多态概念
多态定义
多态的触发机制
虚函数
虚函数表
虚析构函
虚析构函数声明
虚析构函数的作用
纯虚函数
纯虚函数的声明
纯虚函数的作用
抽象类
多态原理
虚函数表 & 虚函数指针
继承机制下的虚函数表
动态绑定
-
多态概念
- 狗狗发出的声音为 -> 旺旺
- 猫猫发出的声音为 -> 喵喵
- 猫猫狗狗我们均可以理解为是动物类,但是其具体对象做同一功能有着不同的表现
-
多态定义
-
多态的触发机制
- 在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)
-
-
-
-