【C++入门】(十九)使用多态和派生类
1. 什么是虚函数成员
-
声明虚成员函数,使用关键字
virtual
-
虚成员函数的工作原理:
(基类和派生类在内存中相邻)
① 创建后的派生类对象中包含基类部分
② 指针被初始化为指向基类的虚函数成员(每个对象都有一个指向虚成员函数表的指针)
③ 调用派生类的构造函数时,将调整指针,指向派生类重写的虚成员函数
④ 使用基类指针时,将根据其指向的对象来指向正确的函数
-
不能通过基类指针访问派生类特有的方法
-
按值传递对象时不能调用虚成员函数,仅当通过指针和引用时才能调用虚成员函数
void Func(Mammal MammalValue) { MammalValue.speak(); } //Mammal speak() void ptrFunc(Mammal* pMammal) { pMammal->speak(); } //speak(); void refFunc(Mammal &rMammal) { rMammal.speak(); } //speak();
2. 如何使用虚析构函数和虚复制构造函数
虚析构函数:
-
virtual ~Mammal() { cout << "virctual ~Mammal()" << endl; }
-
如果类中任何一个函数是虚成员函数,那么析构函数也应该是虚成员函数
虚复制构造函数
-
virtual Mammal* clone() { return new Mammal(*this); }
-
因为构造函数不能为虚,所以要在基类中创建一个 clone() 的虚成员函数
3. 虚成员函数如何让您能够多态地使用基类
-
声明虚成员函数
virtual void Func(){}
-
多态能够将派生对象视为基类对象
#include<iostream> using namespace std; class Mammal { public: void Move() const { cout << "Mammal::Move()" << endl; } //virtual 虚函数 virtual void Speak() const { cout << "Mammal::Spaek()" << endl; } }; class Dog :public Mammal { public: void Move()const { cout << "Dog::Move()" << endl; } void Speak()const { cout << "Dog::Speak()" << endl; } }; int main() { //将一个新Dog对象的地址赋给Mammal指针p Mammal* p = new Dog; p->Move(); //Mammal::Move() //Mammal中的Speak()是虚函数virtual,故调用Dog中重写的Speak() p->Speak(); //Dog::Speak() return 0; }
#include<iostream> using namespace std; class Mammal { public: //虚函数 virtual virtual void speak() const { cout << "Mammal::spaek()" << endl; } }; class Dog :public Mammal { public: void speak()const { cout << "Dog::speak()" << endl; } }; class Cat :public Mammal { public: void speak()const { cout << "Cat::speak()" << endl; } }; class Pig :public Mammal { public: void speak()const { cout << "Pig::speak()" << endl; } }; class Horse :public Mammal { public: void speak()const { cout << "Horse::speak()" << endl; } }; int main() { Mammal* array[5]; Mammal* p; int choice; for (int i = 0; i < 5; i++) { cout << "1Dog,2Cat,3Horse,4Pig" << endl; cin >> choice; switch (choice) { case 1: p = new Dog; break; case 2: p = new Cat; break; case 3: p = new Horse; break; case 4: p = new Pig; break; default: p = new Mammal; break; } //将创建的对象的指针存入数组 array[i] = p; } for (int i = 0; i < 5; i++) { //调用对应的speak()函数 array[i]->speak(); } return 0; }
编译阶段,无法确定调用哪个speak()函数
p指向的对象是在运行阶段确定的,这被称为 后期绑定 / 运行阶段绑定(相对于 静态绑定 / 编译阶段绑定)
运行阶段:晚期绑定;
编译阶段:静态绑定
4. 使用虚成员函数的代价和风险
-
包含虚成员函数的类必须维护一个 v-table,因此会带来一些开销(根据 v-table ,C++知道该调用哪个虚成员函数)
-
如果类很小,且不打算派生出其他类,就不必要用虚成员函数
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具