C++中的多态(虚函数、协变、final、override)函数的重载、重写(覆盖)、隐藏(重定义)的区别

目录

 

多态的概念:不同的对象完成同一行为时,展现出不同的形态。

1、多态的定义及实现

1.1、定义

1.1.1、虚函数的定义

1.1.2、虚函数的重写

1.2、虚函数重写的两个例外

1.2.1、协变

1.2.2、析构函数的重写

2.3、C++11中的final和override

扫描二维码关注公众号,回复: 9189138 查看本文章

2.3.1、final:修饰虚函数表示该函数不再被继承

2.3.2、override:检查派生类的虚函数是否重写了基类的某个虚函数,如果没有重写编译会报错。

2.4、函数的重载、重写(覆盖)、隐藏(重定义)的区别


多态的概念:不同的对象完成同一行为时,展现出不同的形态。

比如买票这件事情,当成人买票是全价,学生买票是半价,儿童不需要买票。不同的对象完成同一件事产生不同的结果。

1、多态的定义及实现

1.1、定义

拥有不同继承关系的对象,在调用相同的类成员时产生出不同的结果。

多态构成的条件:

  • 派生类和基类当中必须有两个函数名,参数列表、返回值相同的虚函数,构成重写关系(派生类中同名函数不加virtual关键字时,由于继承关系系统默认它为虚函数。)
  • 必须通过基类的指针或者引用调用虚函数(因为只用当派生类对象通过指针或者引用转换成基类对象指针时,通过基类指针对象才能将重写后的虚函数调用出来,不这样做基类会调用基类成员,派生类也一样。)
  • 被调用的函数必须是虚函数,并且派生类必须对基类的虚函数进行重写。

1.1.1、虚函数的定义

        在类中被virtual关键字修饰的函数,叫做虚函数。

1.1.2、虚函数的重写

        只有虚函数才会重写,并且满足的条件是:两个虚函数函数名相同、参数列表相同、返回值类型相同、就会构成重写(或者叫覆盖),函数重写之后,基类的虚函数所实现的功能将会被派生类同名虚函数的功能所覆盖。

#include<iosteam>
using namespace std;
class Person {
public:
 virtual void work() { cout << "上课" << endl; }
};
class Student : public Person {
public:
 virtual void work() { cout << "听课" << endl; }
//此处函数如果没有virtual修饰也构成重写关系
};
void Func(Person& p)
{ p.work(); }
int main()
{
 Person ps;
 Student st;
 
 Func(ps);
 Func(st);
 return 0;
}

1.2、虚函数重写的两个例外

1.2.1、协变

派生类重写基类时,两个虚函数的返回值类型不同,基类返回的是基类对象的指针或者引用,派生类返回派生类对象的指针或者引用。通过下面这个例子大家就能理解。

#include <iostream>
using namespace std;
/* 什么叫做协变呢,一共有四个类,A B Person Student 类,B继承自A,Student继承自
Person   当Person中虚函数返回值为A的指针或者引用,Student中的同名虚函数返回B类的
对象指针或者引用的时候两个类构成重写*/
class A 
{};
class B : public A
{};
//class Person 
//{
//public:
//	virtual A* f()
//	{ return new A; }
//};
//class Student : public Person 
//{
//public:
//	virtual B* f() 
//	{
//		 
//		return new B; 
//	}
//};

class Person
{
public:
	virtual Person* f()
	{
		return new Person;
	}
};
class Student : public Person
{
public:
	virtual Student* f()
	{
		 
		return new Student;
	}
};

int main()
{
	A a;
	B b;
	Person p;
	Student s;
	p.f();
	s.f();

	system("pause"); 
	return 0;
}

1.2.2、析构函数的重写

为什么会出现析构函数的重写呢?明明他们俩的函数名并不相同.

此处就要说了,起始所有类的析构函数底层都是通过destructor实现的,在底层他们的名字是相同的。

那它的应用场景是什么呢?

一般情况下我们是不会在类中实现析构函数的,使用的是系统提供的默认析构函数,但是一旦当我们实现之后,系统将会使用用户定义的析构。当我们将派生类指针强转成基类指针时,并且派生类重写了基类虚析构函数,在如下情境下,系统销毁对象时,会先调用派生类的析构函数,再会调用基类的构造函数,保证了正常的析构过程

#include <iostream>
using namespace std;
class Person {
public:
	virtual ~Person() { cout << "~Person()" << endl; }
};
class Student : public Person {
public:
	virtual ~Student() { cout << "~Student()" << endl; }
};
// 只有派生类Student的析构函数重写了Person的析构函数,下面的delete对象调用析构函数,才能构成
//多态,才能保证p1和p2指向的对象正确的调用析构函数。
int main()
{
	Person* p1 = new Person;
	Person* p2 = new Student;
	delete p1;
	delete p2;
	//system("pause");
	return 0;
}

2.3、C++11中的final和override

2.3.1、final:修饰虚函数表示该函数不再被继承

2.3.2、override:检查派生类的虚函数是否重写了基类的某个虚函数,如果没有重写编译会报错。

2.4、函数的重载、重写(覆盖)、隐藏(重定义)的区别

重载:

  • 两个函数在相同的作用域
  • 函数名和参数列表相同

重写:

  • 两个函数分别在派生类和基类作用域
  • 函数名、参数列表、返回值相同(协变、虚析构函数重载)
  • 两个函数是虚函数

隐藏:

  • 两个函数分别在派生类和基类作用域
  • 函数名相同
发布了157 篇原创文章 · 获赞 98 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/weixin_43447989/article/details/102891509
今日推荐