多态和虚函数

一、多态

多态是面向对象程序设计的一个重要特征,多态就是一个东西有多重状态,具有不同功能的函数可以用一个函数名,这样就可以用一个函数名实现不同的功能。

  • 静态多态:静态多态是通过重载实现的,在编译的时候确定调用哪个函数
  • 动态多态:动态多态是利用虚函数实现的,在程序执行期间才动态的确定操作所指对的对象,也称为运行时多态

二、虚函数

在类的继承层次结构中,在不同的层次中可以出现名字相同、参数个数类型相同的函数,这时系统会根据同名覆盖的原则决定调用的对象。

虚函数的作用就是允许在派生类中重新定义与基类同名的函数,并可以通过基类指针或引用来访问基类和派生类中同名函数。

基类的指针是用来指向基类对象的。如果使用基类指针指向了一个派生类的对象,则进行指针类型的转化,即基类指针指向的是派生类中从基类中继承的部分。声明为虚函数之后,在派生类中的虚函数取代了基类原来的虚函数,因此在使用基类指针指向派生类对象后,调用虚函数就是调用了派生类的虚函数。对于非虚函数,调用以后仍然是基类中的函数。

#include<iostream>
#include<string.h>
using namespace std;
class A
{
	private:
		int a;
	public:
		A(){
			a = 1;
		}
		~A(){}
		virtual void display()
		{
			cout<<"a"<<endl;
		}
		void print()
		{
			cout<<"a"<<endl;
		}
} ;
class B:public A{
	public:
		B(){}
		~B(){
		}
		void display()
		{
			cout<<"b"<<endl;
		}
		void print()
		{
			cout<<"b"<<endl;
		}
};
int main()
{

	B b;
	A *a = &b;
	a->display();
	a->print();
}

在这里插入图片描述

三、虚函数表

  • 概念:是一块连续的内存,所有虚函数的首地址都存放在虚函数表中,其大小为4字节。基类指针调用父类的函数还是子类的函数,就是通过虚函数表来实现的。
  • 注意:只有类中有虚函数时,才有虚函数表。父子类之间的虚函数表是不同的地址,且虚函数表中的虚函数的首地址也不同。

在这里插入图片描述从上图可以看出,Derived继承Base,子类Derived实现的虚函数将覆盖父类Base的同名虚函数,如start,stop,另外子类Derived将会继承没有实现的Base虚函数

虚函数的使用注意事项:
(1) 基类方法中声明了方法为虚后,该方法在基类派生类中是虚的。
(2) 若使用指向对象的引用或指针调用虚方法,程序将根据对象类型来调用方法,而不是指针的类型。
(3)如果定义的类被用作基类,则应将那些要在派生类中重新定义的类方法声明为虚。
(4)构造函数不能为虚函数。
(5)基类的析构函数应该为虚函数。
(6)友元函数不能为虚,因为友元函数不是类成员,只有类成员才能是虚函数。
(7)如果派生类没有重定义函数,则会使用基类版本。重新定义继承的方法若和基类的方法不同(协变除外),会将基类方法隐藏;如果基类声明方法被重载,则派生类也需要对重载的方法重新定义,否则调用的还是基类的方法。

虚函数表的工作原理:
编译器给每个对象添加一个指针,存放了指向虚函数表的地址,虚函数表存储了为类对象进行声明的虚函数地址。比如基类对象包含一个指针,该指针指向基类所有虚函数的地址表,派生类对象将包含一个指向独立地址表的指针,如果派生类提供了虚函数的新定义,该虚函数表将保存新函数的地址,如果派生类没有重新定义虚函数,该虚函数表将保存函数原始版本的地址。如果派生类定义了新的虚函数,则该函数的地址将被添加到虚函数表中,注意虚函数无论多少个都只需要在对象中添加一个虚函数表的地址。

三、纯虚函数

纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。类似于java中的接口。含有纯虚函数的类称为抽象类,它不能生成对象。

四、析构函数和虚函数

此时只是释放了基类的资源,而没有调用派生类的析构函数。调用函数执行的也是基类定义的函数。这样的删除只能够删除基类对象,而不能删除子类对象,形成了删除一半形象,造成内存泄漏。

#include<iostream>
#include<string.h>
using namespace std;
class A
{
	private:
		int a;
	public:
		A(){
			cout<<"constructor A"<<endl;
		}
		~A(){
			cout<<"destructor A"<<endl;
		}
} ;
class B:public A{
	public:
		B(){
			cout<<"constructor B"<<endl;
		}
		~B(){
			cout<<"destructor B"<<endl;
		}
};
int main()
{

	A *a = new B;
	delete a;
	return 0;
}

在这里插入图片描述当我们把基类的析构函数改为虚函数的时候,就可以避免这种内存泄露。

#include<iostream>
#include<string.h>
using namespace std;
class A
{
	private:
		int a;
	public:
		A(){
			cout<<"constructor A"<<endl;
		}
		virtual ~A(){
			cout<<"destructor A"<<endl;
		}
} ;
class B:public A{
	public:
		B(){
			cout<<"constructor B"<<endl;
		}
		~B(){
			cout<<"destructor B"<<endl;
		}
};
int main()
{

	A *a = new B;
	delete a;
	return 0;
}

在这里插入图片描述注意:在非继承的类中,不要把析构函数设置为虚函数。因为这样会增加一个虚函数表占用空间。

C++中的继承、多态虚函数是面向对象编程的重要概念。 继承是指一个类可以从另一个类继承属性和方法。子类可以继承父类的公有成员和保护成员,但不能继承私有成员。通过继承,子类可以重用父类的代码,并且可以添加自己的特定功能。继承可以实现代码的重用和层次化的设计。 多态是指同一个函数可以根据不同的对象调用不同的实现。多态可以通过虚函数来实现。虚函数是在基类中声明为虚拟的函数,它可以在派生类中被重写。当通过基类指针或引用调用虚函数时,实际调用的是派生类中的实现。这样可以实现动态绑定,即在运行时确定调用的函数。 虚函数的原理是通过虚函数表来实现的。每个包含虚函数的类都有一个虚函数表,其中存储了虚函数的地址。当调用虚函数时,编译器会根据对象的类型在虚函数表中查找对应的函数地址并调用。 综上所述,C++中的继承、多态虚函数是实现面向对象编程的重要机制,它们可以提高代码的灵活性和可扩展性。 #### 引用[.reference_title] - *1* *3* [C++多态虚函数虚函数表](https://blog.csdn.net/weixin_46053588/article/details/121231465)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [c++多态虚函数表内部原理实战详解](https://blog.csdn.net/bitcarmanlee/article/details/124830241)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值