多态

本文深入探讨C++中的多态概念,包括多态的构成条件、虚函数的作用及重写规则,同时讲解了抽象类的概念及其应用场景,通过具体代码示例帮助理解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

注意:以下所有代码都是在VS2013平台

一、多态概念

多态按字面的意思就是多种形态。同一个事物在不同的场合下具有多种形态。通俗的讲就是,见人说人话,见鬼说鬼话。 

举个栗子:同样是吃饭的行为,有的人吃盖浇饭,有的人吃面条。可见,同一种行为得到不同的结果。

二、多态构成条件

继承那块提到继承最大的作用就是实现多态。所以多态就是不同的继承关系的类对象,去调用同一个函数。除此之外还需要满足两个条件:

  1. 基类中必须有虚函数,派生类对基类中的虚函数重写
  2. 通过基类对象的引用或者指针调用虚函数
  • 什么是虚函数?

在类的成员函数前面加上virtual关键字,构成虚函数。

  • 什么是虚函数的重写?

首先重写,一定是重写虚函数,并且是派生类重写基类的虚函数。重写:派生类中有一个和基类中虚函数完全相同的一个函数,我们就称为派生类的虚函数重写了基类的虚函数。此处完全相同指:函数名、返回值、参数列表完全相同,但是有两个特例。

基类和派生类的虚函数访问权限可以不相同,但是基类的虚函数必须是public。

其实派生类对基类的虚函数重写不是必须要加virtual关键字,为了规范起见,一般加上virtual。 

//多态
class Base
{
public:
	virtual void TestFunc1()
	{
		cout << "Base::TestFunc1()" << endl;
	}
public:
	int _b;
};

class Derived:public Base
{
public:
	virtual void TestFunc1()
	{
		cout << "Derived::TestFunc1()" << endl;
	}
public:
	int _d;
};

void TestVirtual(Base& b)
{
	b.TestFunc1();
}

int main()
{
	Base b;
	Derived d;
	TestVirtual(b);
	TestVirtual(d);
	return 0;
}

 

特例1:协变

协变:重写的虚函数返回值可以不相同。但是必须分别是(1)基类指针和派生类指针(2)基类引用和派生类引用

//协变
class Base
{
public:
	virtual Base* TestFunc1()
	{
		cout << "Base::TestFunc1()" << endl;
		return this;
	}
public:
	int _b;
};

class Derived:public Base
{
public:
	virtual Derived* TestFunc1()
	{
		cout << "Derived::TestFunc1()" << endl;
		return this;
	}
public:
	int _d;
};

void TestVirtual(Base& b)
{
	b.TestFunc1();
}

int main()
{
	Base b;
	Derived d;
	TestVirtual(b);
	TestVirtual(d);
	return 0;
}

特例2:析构函数的重写

基类中的析构函数如果是虚函数,那么派生类的析构函数就重写了基类的析构函数。在这里重写函数名不相同,看起来违背了重写定义,其实编译器在底层对两个析构函数名做了统一的处理。在实际中,基类的析构函数最好给为虚函数,否则会造成内存泄漏。

//析构函数重写
class Base
{
public:
	virtual ~Base()
	{
		cout << "Base::~Base()" << endl;
	}
public:
	int _b;
};

class Derived:public Base
{
public:
	virtual ~Derived()
	{
		cout << "Derived::~Derived()" << endl;
	}
public:
	int _d;
};

int main()
{
	Base *pb = new Base;
	Base *pd = new Derived;

	delete pb;//调用基类的析构函数
	delete pd;//先调用派生类自己的析构函数,最好一条语句结束之后调用基类的析构函数
	return 0;
}

  • 如果基类析构函数不是虚函数,那么为什么会造成内存泄漏?
class Base
{
public:
	~Base()//基类的析构函数不是虚函数
	{
		cout << "Base::~Base()" << endl;
	}
public:
	int _b;
};

class Derived :public Base
{
public:
	Derived()
	{
		_d = new int[10];
	}
	~Derived()
	{
		cout << "Derived::~Derived()" << endl;
		if (_d)
		{
			delete[] _d;
			_d = nullptr;
		}
	}
public:
	int* _d;
};

int main()
{
	Base *pb = new Derived;
	delete pb;//析构函数没有重写,所以调用基类的析构函数,
	//造成派生类所申请的内存空间没有释放,导致内存泄漏
	//所以,我们应该吧基类的析构函数给为虚函数。
	return 0;
}

三、同名隐藏、重写、重载

  1. 同名隐藏:(1)作用域分别是基类和派生类(2)成员名相同
  2. 重写:(1)作用域分别是基类和派生类(2)两个函数都是虚函数(3)函数名/参数/返回值相同,除了两个特例
  3. 重载:(1)相同作用域(2)函数名相同,参数列表不同(参数个数,次序,类型)

四、抽象类

如果一个虚函数后面加上  =0  ,则这个函数称为纯虚函数,包含纯虚函数的类称为抽象类。抽象类不可以实例化对象。派生类继承抽象类之后,也不可以实例化对象。只有派生类对基类的纯虚函数重写,派生类才能实例化对象。所以抽象类的提出更体现出了接口继承。

//抽象类演示
class WC
{
public:
	virtual void GotoWC() = 0;
};

class WomenWC :public WC
{
public:
	virtual void GotoWC()
	{
		cout << "go to women wc" << endl;
	}
};

class ManWC:public WC
{
public:
	virtual void GotoWC()
	{
		cout << "go to man wc" << endl;
	}
};

int main()
{
	WC *wp1 = new WomenWC;
	WC *wp2 = new ManWC;

	wp1->GotoWC();
	wp2->GotoWC();
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_41318405

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值