【C++】多态

【C++】多态,第1张

目录

多态的定义:

多态的原理:

纯虚函数:

虚析构和纯虚析构:


多态的定义:

首先我们先来了解一下百度百科中定义的多态:指为不同数据类型的实体提供统一的接口。多态类型可以将自身所支持的 *** 作套用到其它类型的值上。计算机程序运行时,相同的消息可能会送给多个不同的类别之对象,而系统可依据对象所属类别,引发对应类别的方法,而有不同的行为。简单来说,所谓多态意指相同的消息给予不同的对象会引发不同的动作。

即对于不同的实现方式有统一的入口,进入入口后可以自行选择不同的实现方式,不同于以往一个入口对应一种实现方式。C++中多态也具体分为两种:静态多态和动态多态

静态多态实现于函数重载,运算符重载等,它们复用同一个函数名。相同的符号给予不同的含义。

动态多态实现于:虚函数和派生类。

两者的区别是函数地址的绑定,静态多态在编译阶段就决定哪个函数名对于哪一种实现方式;而动态多态的函数名晚绑定,它是根据具体 *** 作才决定使用哪一种实现方式。

下面举个虚函数实现动态多态的例子:思路是创建一个动作的父类,这个入口可以提供一个行为:卖书。下面创造两个子类,它们分别对应新书和旧书,它们继承父类的成员,(为什么要创建父类子类?)因为父类作为接收子类的一个入口,用父类作为接收类型,即入口,再根据后续 *** 作选择 *** 作哪一个子类。

创建一个函数,这个函数形参为父类的引用。这里的父类相当于一个接口,根据传进去的子类来决定实现方式。此时要发生多态的 *** 作,所以我们还需要在父类与子类相同的函数名称前加上关键字virtual。

class Action
{
public:
	virtual void sell()
	{
		cout << "卖书" << endl;
	}
};

class Newbook:public Action
{
public:
	void sell()
	{
		cout<<"卖新书" << endl;
	}
};

class Oldbook :public Action
{
public:
	void sell()
	{
		cout<<"卖旧书" << endl;
	}
};

void _sell(Action &action)
{
	action.sell();
}

int main()
{
	Oldbook oldbook;
	Newbook newbook;
	_sell(oldbook);
	_sell(newbook);
	return 0;
}

这里要实现多态需要满足几个条件:

1.有继承关系。即要通过父类这个接口,选择子类

2.子类要重写父类的虚函数。即要想发生多态,需要子类和父类中有相同的函数名(公共接口,父类子类都有这个函数名,但是不同子类间具体实现可以不同,函数名作为细线将子类和父类串起来,父类的函数名需要加关键字变成虚函数,作为一个头结点,其他子类的重名函数都是它发散出去的线。)

3.父类指针或者引用指向子类对象。

多态的原理:

首先是父类的创建,创建一个父类后,为这个父类写入一个函数实现,那么此时父类的大小是1;但是加上一个virtual关键字后,父类的内存空间变成4,此时存在父类里面的是一个指针vfptr,它叫虚函数指针,指向一个虚函数列表,通过虚函数列表我们可以找到原来在父类中写入的函数。当我们创造一个子类,子类如果不重写函数,那么此时子类会继承父类的指针,子类也可以通过虚函数表找到对应函数实现。但要是我们重写了函数,子类中的虚函数表里会记录子类改写的函数的地址,而非原来父类的函数地址。此时当用父类的指针或引用指向子类对象时,就发生了多态。

纯虚函数:

在设计实现多态中的父类时,父类中的虚函数往往没有什么实际的作用,为了简便,我们使用纯虚函数的写法来写父类中的函数。含有纯虚函数的类叫做抽象类。

纯虚函数的语法是:virtual + 函数类型 + 函数名 + () = 0 ;

采用纯虚函数,我们要注意一些事项:

1.纯虚函数所在的类不能进行实例化对象,即不能创造一个新的对象;

2.子类继承父类一定要对纯虚函数进行重写,否则子类也是一个抽象类;

class Fath
{
public:
	virtual void Show()= 0;
};

class Son :public Fath
{
public:
	void Show()
	{
		cout << "test" << endl;
	}
};

int main()
{
	Fath* fath = new Son;
	fath->Show();
	return 0;
}

虚析构和纯虚析构:

首先我们来看一种场景:如果子类中需要开辟堆区的数据,而我们又同时需要发生多态,此时子类开辟的堆区空间可能就没有能够进行释放。这就需要将父类的析构函数改成虚析构函数。与之前的虚函数一致。

class Person
{
public:
	virtual void name() = 0;
	virtual ~Person()
	{
		cout << "Person析构函数调用" << endl;
	}
};


class P1:public Person
{
public:
	void name()
	{
		m_name = new string("梁");
		cout << *m_name << endl;
	}
	~P1()
	{
		cout<<"P1析构函数调用" << endl;
		if (m_name != NULL)
		{
			delete m_name;
			m_name = NULL;
		}
	}
	string* m_name;
};

void test()
{
	Person* People = new P1;
	People->name();
	delete People;
}

int main()
{
	test();
	return 0;
}

纯虚析构函数与纯虚函数也有不同,纯虚析构既需要声明,也需要实现:

class Person
{
public:
	virtual void name() = 0;
	virtual ~Person() = 0;
};

Person::~Person()
{
	cout<<"纯虚析构函数调用" << endl;
}

虚析构和纯虚析构的应用场景都是用于解决子类中需要开辟堆区空间,而需要调用子类中的析构函数释放子类开辟的堆区空间的情况。其他情景下可以不用。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/langs/921558.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-05-16
下一篇2022-05-16

发表评论

登录后才能评论

评论列表(0条)

    保存