【C++ | 虚函数】虚函数详解 及 例子代码演示(包含虚函数使用、动态绑定、虚函数表、虚表指针)
😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
🤣本文内容🤣:🍭介绍C++的虚函数 🍭
😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你——泰戈尔🍭
⏰发布时间⏰:2024-07-13 14:33:55
本文未经允许,不得转发!!!
目录
- 🎄一、什么是虚函数?为什么需要虚函数?
- 🎄二、静态绑定、动态绑定
- 🎄三、虚函数的使用
- ✨3.1 虚函数在基类的定义
- ✨3.2 虚函数在派生类的使用
- ✨3.3 final 和 override 说明符
- 🎄四、虚函数表、虚表指针
- ✨4.1 虚表指针
- ✨4.2 虚函数表
- 🎄五、总结
🎄一、什么是虚函数?为什么需要虚函数?
定义:虚函数就是在函数声明时使用关键字virtual修饰的成员函数。其格式一般如下:
class CAnimal{ virtual void eat(); // 声明了虚函数 eat() }
为什么需要虚函数?
在C++中使用虚函数的主要目的是实现多态。多态是多种形态的意思,也就是同一个方法在派生类和基类中的行为是不同的。
C++怎样通过虚函数实现多态的呢?一般情况下,通过指针或引用调用一个类成员函数时,程序会 根据引用类型或指针类型 选择对应类的成员函数,如果被调用的成员函数是虚函数的话,则程序将根据 引用或指针指向的对象 的类型来选择方法。
下面用代码演示基类指针调用 正常函数 和 虚函数 的区别:
// g++ 23_Virtual.cpp #include using namespace std; class CAnimal{ public: CAnimal(){ cout cout cout public: CDog(){ cout // 重写了基类的eat(),基类的会隐藏 cout cout CAnimal animal; CDog dog; CAnimal* pAnimal = &animal; // 基类指针指向基类对象 pAnimal-eat(); // 调用非虚函数,按照指针类型调用,打印 Animal eat pAnimal-run(); // 调用虚函数,按照对象类型调用,打印 Animal run cout public: virtual void run(){ cout public: virtual void run() override{ cout const int OBJ_NUM = 4; CAnimal *pAnimal[OBJ_NUM]; // 基类指针数组,用于管理对象 // 程序运行后,根据输入类型创建对象 char kind; // 用于获取类型 for(int i=0; i cout pAnimal[i]-run(); } for(int i=0; i delete pAnimal[i]; // 释放对象 } return 0; } public: //virtual CAnimal(){ // 5、构造函数不能设置虚函数 CAnimal(){ cout // 2、派生类的eat()可能不同实现,设置成虚函数 cout strncpy(m_name, name, sizeof(m_name)); } virtual ~CAnimal(){ // 6、基类的析构函数声明为虚函数 cout public: CDog(){ cout // 重写了基类的eat() cout cout CAnimal* pAnimal = new CAnimal(); // 基类指针指向基类对象 pAnimal-eat(); delete pAnimal; pAnimal = NULL; cout public: virtual void eat(int x) const; // 虚函数 }; class CDog: public CAnimal{ public: virtual void Eat(int x); // e 写成 E,函数名不一致,新的虚函数 virtual void eat(short x); // 参数列表不一样,新的虚函数 virtual void eat(int x); // const 属性不一样,新的虚函数 virtual void eat(int x) const; // 函数名,参数列表,const属性都一致,重写了基类的虚函数 } public: virtual CAnimal *run(int a) const; }; class CDog: public CAnimal{ public: virtual CDog *run(int a) const; // 重写了基类的 run ,但返回值为 CDog* } public: CAnimal(){ cout cout cout cout public: CDog(){ cout }; // e 写成 E,函数名不一致,新的虚函数 virtual void eat(short x){}; // 参数列表不一样,新的虚函数 virtual void eat(int x){}; // const 属性不一样,新的虚函数 virtual void eat(int a) const{ // 重写了基类的eat() cout // 重写了基类的 run ,但返回值为 CDog* cout cout CAnimal* pAnimal = new CAnimal(); // 基类指针指向基类对象 CAnimal* pRun = pAnimal-run(1); pAnimal-eat(1); delete pAnimal; pAnimal = NULL; cout public: virtual void finlaFun(int a) const final; // 指定为final,不允许被重写 }; class CDog : public CAnimal{ public: virtual void finlaFun(int a) const{} // 基类指定为final了,重写会报错 }; public: virtual void eat(int x) const; // 虚函数 }; class CDog: public CAnimal{ public: virtual void Eat(int x)override{}; // e 写成 E,函数名不一致,报错 virtual void eat(short x)override{}; // 参数列表不一样,报错 virtual void eat(int x)override{}; // const 属性不一样,报错 virtual void eat(int a) const override{ // 重写了基类的eat() cout public: CAnimal(){ cout // 指定为final,不允许被重写 cout cout cout cout public: CDog(){ cout } // 基类指定为final了,重写会报错 virtual void Eat(int x)override{}; // e 写成 E,函数名不一致,报错 virtual void eat(short x)override{}; // 参数列表不一样,报错 virtual void eat(int x)override{}; // const 属性不一样,报错 #endif virtual void eat(int a) const override{ // 重写了基类的eat() cout // 重写了基类的 run ,但返回值为 CDog* cout cout CAnimal* pAnimal = new CAnimal(); // 基类指针指向基类对象 delete pAnimal; pAnimal = NULL; cout // 空类 }; class CAnimal{ // 基类,有一个虚函数 public: virtual void eat(){} }; class CDog : public CAnimal{// 派生类,继承了基类的虚函数 }; int main () { CAnimal_NULL animal_null; CAnimal animal; CDog dog; cout public: virtual void eat(){ cout cout public: virtual void run() override{ // 重写,会使用新的虚函数地址 cout cout CAnimal animal; CDog dog; // 获取虚表指针,类对象第一个指针大小的内存里的值 unsigned long* vptr_animal = (unsigned long*)(*((unsigned long*)(&animal))); unsigned long* vptr_dog = (unsigned long*)*(unsigned long*)&dog; // 打印虚函数表各个虚函数地址 cout cout cout
文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。