【C++ | 虚函数】虚函数详解 及 例子代码演示(包含虚函数使用、动态绑定、虚函数表、虚表指针)

07-17 551阅读

😁博客主页😁:🚀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 虚函数表
      • 🎄五、总结

        【C++ | 虚函数】虚函数详解 及 例子代码演示(包含虚函数使用、动态绑定、虚函数表、虚表指针)

        【C++ | 虚函数】虚函数详解 及 例子代码演示(包含虚函数使用、动态绑定、虚函数表、虚表指针)

        🎄一、什么是虚函数?为什么需要虚函数?

        定义:虚函数就是在函数声明时使用关键字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 
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]