当前位置: 首页 > news >正文

28.C++多态1 (多态的概念与简单使用,虚函数,final,override)

⭐上篇文章:27.C++继承 3 (复杂的菱形继承与菱形虚拟继承)-CSDN博客

⭐本篇代码:c++学习/17.C++三大特性-多态 · 橘子真甜/c++-learning-of-yzc - 码云 - 开源中国 (gitee.com)

⭐标⭐是比较重要的部分

目录

一. C++多态简介

1.1 构成多态的两个必要条件 

二. virtual关键字与虚函数 ⭐

 1.1 使用虚函数测试多态

1.2 virtual关键字的用法

三. 虚函数重写的两个例外 

3.1 协变

3.2 析构函数 ⭐

四. final 和 override关键字 

        4.1 final 

4.2 override 


一. C++多态简介

        多态是指不同有继承关系的对象在调用同一个函数之后,会有不同的结果。比如学生票和成人票。

        Student继承了Person,但是Studenet买票是半价,而Person是全价

1.1 构成多态的两个必要条件 

1 必须通过基类的指针或者引用去调用虚函数

2 被调用的函数必须是虚函数,且派生类必须完成对虚函数的重写

二. virtual关键字与虚函数 ⭐

        用virtual修饰的成员函数就是虚函数

比如:下面代码中的buyTickets就是虚函数

#include <iostream>
using namespace std;

class Person
{
public:
	virtual void buyTickets()
	{
		cout << "全价买票" << endl;
	}
};

int main()
{

}

 1.1 使用虚函数测试多态

        

#include <iostream>
using namespace std;

class Person
{
public:
	virtual void buyTickets()
	{
		cout << "全价买票" << endl;
	}
};

class Student : public Person
{
public:
	virtual void buyTickets()
	{
		cout << "半价买票" << endl;
	}
};

void test(Person& tmp)
{
	tmp.buyTickets();
}

int main()
{
	Person p1;
	Student s1;

	Person* tmp1 = &p1;
	Student* tmp2 = &s1;

	//测试指针
	cout << "使用指针完成多态" << endl;
	tmp1->buyTickets();
	tmp2->buyTickets();


	//测试引用
	cout << "使用引用完成多态" << endl;
	test(p1);
	test(s1);
	return 0;
}

测试结果如下:

        可以看到,我们使用基类的指针或者引用成功的调用了基类和派生类的函数,从而达到一种多态的效果 

1 如果两个有继承关系的成员函数满足多态的条件,指针指向哪个对象或者引用哪个对象就调用这个对象的成员函数

2 如果不满足多态条件,调用的类型对象是谁,就调用该对象的成员函数

3 重写的虚函数需要保持返回值类型,名称,参数列表都一致

1.2 virtual关键字的用法

1 virtual修饰类的继承中,完成虚继承,解决菱形继承中的冗余性和二义性

2 virtual修饰成员函数,变为虚函数完成函数的重写

三. 虚函数重写的两个例外 

3.1 协变

        如果派生类重写虚函数的时候,两个函数的返回值类型不同,但是都为本身类的指针或者引用的时候。也构成虚函数的重写

如下面的代码,基类返回基类的指针,派生类返回派生类的指针 

#include <iostream>
using namespace std;

class Person
{
public:
	virtual Person* buyTickets()
	{
		cout << "全价买票" << endl;
		return this;
	}
};

class Student : public Person
{
public:
	virtual Student* buyTickets()
	{
		cout << "半价买票" << endl;
		return this;
	}
};

void test(Person& tmp)
{
	tmp.buyTickets();
}

int main()
{
	Person p1;
	Student s1;

	Person* tmp1 = &p1;
	Student* tmp2 = &s1;

	//测试指针
	cout << "使用指针完成多态" << endl;
	tmp1->buyTickets();
	tmp2->buyTickets();


	//测试引用
	cout << "使用引用完成多态" << endl;
	test(p1);
	test(s1);
	return 0;
}

3.2 析构函数 ⭐

        如果有继承关系,并且我们需要使用多态的时候。需要使用virtual修饰析构函数,将其定义为虚函数。        

        否则当我们使用基类的指针或者引用之后,销毁这个变量的时候就会由于隐藏只调用当前对象的析构函数。

比如:

#include <iostream>
using namespace std;

class A
{
public:
	virtual void test(){}
	 ~A() { cout << " ~A()" << endl; }
};
class B :public A
{
public:
	virtual void test() {}
	 ~B() { cout << " ~B()" << endl; }
};



int main()
{
	//使用多态
	A* a = new A();
	A* b = new B();
	a->test();
	b->test();
	
	//删除变量
	delete a;
	delete b;
	return 0;
}

        在上面的代码中,我们使用多态去调用test。然后使用delete删除变量。正常来说,析构函数的调用顺序是 ~A ()        ~B()         ~A()

测试结果如下:

        可以看到,只调用了A的析构函数。这是因为我们没有对析构函数进行虚函数重写,导致B类的析构函数和A类的析构函数构成隐藏,又由于我们的指针定义的时候是A,所以不会调用B的析构函数,从而导致内存泄漏

对析构函数进行重写

#include <iostream>
using namespace std;

class A
{
public:
	virtual void test(){}
	virtual ~A() { cout << " ~A()" << endl; }
};
class B :public A
{
public:
	virtual void test() {}
	virtual ~B() { cout << " ~B()" << endl; }
};



int main()
{
	//使用多态
	A* a = new A();
	A* b = new B();
	a->test();
	b->test();
	
	//删除变量
	delete a;
	delete b;
	return 0;
}

可以看到,成功的调用了B的析构函数。解决了内存泄漏问题

四. final 和 override关键字 

        4.1 final 

final有两个用法

final修饰类,这个类不能被继承。

final修饰成员函数,这个成员函数不能被重写

修饰虚函数 

 修饰类

 

4.2 override 

        override修饰成员函数,会检查这个成员函数是否重写了父类的函数,如果不构成重写,就会显示报错

举例:

#include <iostream>
using namespace std;

class A 
{
public:
	virtual void test()
	{}

	void f(){}
};

class B :public A
{
public:
	virtual void  test() override 
	{}

	virtual void f()override
};



int main()
{
	return 0;
}

无法通过编译 

相关文章:

  • linux--多进程开发(5)--进程
  • C/C++ | 每日一练 (4)
  • Linux:基础IO
  • 使用 pytest-mock 进行 Python 高级单元测试与模拟
  • 为什么MySQL选择使用B+树作为索引结构
  • 51串口通信实例(蓝牙模块和WiFi模块的介绍和使用)
  • 什么是零拷贝?
  • 模型蒸馏与量化技术:让AI模型“瘦身”却不“降智”的底层逻辑
  • Windows Server 搭建 RADIUS 认证服务器
  • 解决安卓recyclerView滚到底部不彻底问题
  • LeetCode每日精进:622.设计循环队列
  • Linux 病毒扫描工具 ClamAV 使用
  • 4*A100 部署 deepseek-r1-671B
  • Vue 报错error:0308010C:digital envelope routines::unsupported 解决方案
  • Web自动化之Selenium下Chrome与Edge的Webdriver常用Options参数
  • 【中等】59.螺旋矩阵Ⅱ
  • DeepSeek个人知识库
  • vscode设置自动换行
  • pnpm的基本用法
  • 【设计师专属】智能屏幕取色器Pro|RGB/HEX双模式|快捷键秒存|支持导出文档|C++ QT
  • 央视网评广东校服自愿原则:斩断“统一着装”背后的利益脐带
  • 2025年上海市工程建设标准国际化工作要点发布
  • 斗鱼一季度直播收入降近三成,语音社交服务推高广告等收入,称将持续打击涉赌行为
  • 视频|黎明:年轻人已经不相信爱情了吗?
  • 完善劳动关系协商协调机制,《共同保障劳动者合法权益工作指引》发布
  • 波兰总统选举投票开始,将是对亲欧路线的一次严峻考验