首页 > 编程语言 >C++:多态

C++:多态

时间:2024-11-22 18:45:36浏览次数:3  
标签:函数 基类 多态 C++ class 派生类 重写 public

目录

一、多态的概念

二、多态的定义及实现

1、多态的构成条件

2、虚函数

3、虚函数的重写

3.1、协变:

3.2、析构函数重写:

4、override 和 final 关键字

5、重载、覆盖、隐藏

三、抽象类

1、接口继承

2、实现继承


 

一、多态的概念

顾名思义,多态就是多种形态,举个例子:比如说买票这个行为,当普通人买票时是全价票,学生买票时是半价票,军人买票时是优先买票。也就是说一件事,每个不同的类去做,会发生不一样的行为称之为多态。

二、多态的定义及实现

1、多态的构成条件

1、多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如 Student 继承了Person。 Person 对象买票全价, Student 对象买票半价。 2、必须通过基类的指针或者引用调用虚函数。 3、被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。

2、虚函数

虚函数就是被virtual修饰的类成员函数成为虚函数。

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

3、虚函数的重写

当派生类中和基类有一个完全相同的虚函数。派生类和基类都得写才标准。
class Person {
public:
    virtual void BuyTicket()
    {
        cout << "买票-全价" << endl;
    }
};
 
class Student : public Person {
public:
    virtual void BuyTicket()
    {
        cout << "买票-半价" << endl;
    }
 
    //void BuyTicket() { cout << "买票-半价" << endl}
};
 
void Func(Person& p)
{
    p.BuyTicket();
}
 
int main()
{
    Person ps;
    Func(ps);
 
    Student st;  
    Func(st);
 
    return 0;
}

3.1、协变:

派生类重写基类虚函数时,与基类虚函数返回的类型不同。

3.2、析构函数重写:

如果基类的析构函数是虚函数,那么派生类的析构函数默认会和基类的析构函数构成重写,虽然函数名不同,但是编译器进行编译后,把析构函数统一处理为destructor。

class Person
{
public:
	virtual void BuyTicket()
	{
		cout << "全价" << endl;
	}
	virtual Person* F()
	{
		return new Person;
	}
	virtual ~Person()
	{
		cout << "~Person" << endl;
	}
};
class Student : public Person
{
public:
	virtual void BuyTicket()
	{
		cout << "半价" << endl;
	}
	virtual Student* F()
	{
		return new Student;
	}
	virtual ~Student()
	{
		cout << "~Student" << endl;
	}
};
void Test(Person& p)
{
	p.BuyTicket();
}
int main()
{
	Person p;
	Student s;
	Test(p);
	Test(s);
	return 0;
}析构了两次~Person

 

class Person {
public:
	~Person()
	{
		cout << "~Person()" << endl;
	}
};

class Student : public Person {
public:
	~Student()
	{
		cout << "~Student()" << endl;
	}
};

int main()
{
	Person* p1 = new Person;
	Person* p2 = new Student;

	delete p1;
	delete p2;

	return 0;
}

为什么会调用两次~Person()? 而且没有调用~Student()。子类的指针赋给父类指针时,会发生切割。p2 指针只会指向属于父类的那一部分,子类的析构函数自然也就没有赋值过去。所以调用的是父类的析构函数, 并不能正确的释放掉动态开辟的空间。只有派生类 Student 的析构函数重写了 Person 的析构函数,下面的 delete 对象调用析构函数,才能构成多态,才能保证 p1 和 p2 指向的对象正确的调用析构函数。 修改后:
class Person {
public:
    virtual ~Person()
    {
        cout << "~Person()" << endl;
    }
};
 
class Student : public Person {
public:
    virtual ~Student()
    {
        cout << "~Student()" << endl;
    }
};
 
int main()
{
    Person* p1 = new Person;
    Person* p2 = new Student;
 
    delete p1;
    delete p2;
 
    return 0;
}

4、override 和 final 关键字

C++11中, 用来辅助进行虚函数多态的多种复杂情况,避免出现疏忽而导致错误的情况出现。

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

class Person
{
public:
	/*virtual*/ void BuyTicket()
	{
		cout << "全价票" << endl;
	}
};
class Student : public Person
{
public:
	virtual void BuyTicket() override//报错
	{
		cout << "半价票" << endl;
	}
};
class Child : public Student
{
public:
	virtual void BuyTicket() override
	{
		cout << "半价票" << endl;
	}
};

final : 修饰虚函数,表示这个虚函数不能被重写。

class Person
{
public:
	virtual void BuyTicket()
	{
		cout << "全价票" << endl;
	}
};
class Student : public Person
{
public:
	virtual void BuyTicket() final
	{
		cout << "半价票" << endl;
	}
};
class Child : public Student
{
public:
	virtual void BuyTicket()//
	{
		cout << "半价票" << endl;
	}
};

5、重载、覆盖、隐藏

1.重载指的是,函数名在一个作用域,并且函数名相同,参数不同的情况,那么这两个函数就构成了函数重载,编译器在进行处理的时候会根据参数形成不同的函数表。

2.重写指的是,两个函数在基类和派生类的作用域下,前提是函数名、参数、返回值都一样的情况下,如果是虚函数,那么就构成了重写,其中子类可以不写virtual,可以理解为虚函数的属性被从基类中继承了下来,但是并不推荐这样写,其中要注意特殊情况,比如协变和析构函数的情况。

3.隐藏指的是,两个函数在基类和派生类的作用域下,当函数名相同的时候,如果不符合重写的定义那么就是重定义了,比如在继承中见到的很多种情况。

三、抽象类

抽象类的定义:在虚函数后面写上=0,就是纯虚函数,有纯虚函数的类就是抽象类,特点是不能实例化出一个具体的对象派生类也是不能,只有在重写了虚函数,才能实例化,纯虚函数体现了派生类要重写的这个原则,同时也体现了接口继承的概念。

基类 - 抽象类 - 不能实例化出对象
class Car
{
public:
    virtual void Drive() = 0; 纯虚函数,不需要实现它
};
 
派生类
class Benz :public Car
{
public:
    virtual void Drive() 必须重写基类虚函数,派生类才能实例化出对象
    {
        cout << "Benz-舒适" << endl;
    }
};
 
class BMW :public Car
{
public:
    virtual void Drive()
    {
        cout << "BMW-操控" << endl;
    }
};
 
int main()
{
    基类是抽象类,不能实例化出对象,但可以定义基类指针,用来实现多态
    Car* pBenz = new Benz;
    pBenz->Drive();
 
    Car* pBMW = new BMW;
    pBMW->Drive();
 
    return 0;
}

1、接口继承

接口继承(Interface Inheritance)是指从一个纯虚基类(pure virtual base class)继承而来,目的是为了实现一个类的接口,使得派生类必须实现该接口中定义的所有纯虚函数。接口继承的主要目的是实现类的接口复用,它并不关心实现细节。在接口继承中,派生类只需要实现基类中定义的纯虚函数,不需要关心基类中其他的数据和函数。

class Shape
{
public:
	virtual void draw() = 0;//纯虚函数
};
class Cirrle : public Shape
{
public:
	void draw() override
	{
		//实现圆形的绘画
	}
};
class Squre : public Shape
{
	void draw() override
	{
		//实现矩形的绘画
	}
};

2、实现继承

实现继承(Implementation Inheritance)是指从一个普通的基类(非纯虚基类)继承而来,目的是为了实现基类中已有的函数或数据。实现继承的主要目的是实现代码复用,它关心基类中的实现细节。在实现继承中,派生类会继承基类中所有的成员函数和数据成员,并且可以重写这些函数以改变它们的行为。

class Person
{
public:
	virtual void Say()
	{
		cout << "Person" << endl;
	}
};
class Student : public Person
{
public:
	virtual void Say() override
	{
		cout << "Student" << endl;
	}
};

int main()
{
	Person p;
	Student s;
	s.Say();
	return 0;
}

下章节介绍多态的原理!如有不正之处,希望大家私信我去改正! 

标签:函数,基类,多态,C++,class,派生类,重写,public
From: https://blog.csdn.net/2201_75956982/article/details/143978352

相关文章

  • 什么是 C++ 中的智能指针?有哪些类型的智能指针?
    智能指针的定义在C++中,智能指针是一种类模板,用于管理动态分配的内存。它的主要目的是自动管理内存的生命周期,避免手动释放内存时可能出现的错误,如内存泄漏(忘记释放内存)和悬空指针(访问已释放的内存)。智能指针通过重载*(解引用运算符)和->(成员访问运算符)等运算符,使得它在行为......
  • 打卡信奥刷题(288)用C++工具信奥P2242[普及组/提高] 公路维修问题
    公路维修问题题目描述由于长期没有得到维修,A国的高速公路上出现了nnn个坑。为了尽快填补好这n......
  • 零基础同时入门并掌握C语言和C++——第一节——选择开发环境
    本系列文章将针对C语言使用VisualStudio2022, C++使用DevC++作为开发环境进行讲解。下面分别讲述选择这两款开发环境的原因和好处:DevC++市面上有很多版本,常见的有蓝色(也就是图片中展示的这款)红色,和小熊猫等。对于初学者来说可能会纠结究竟下载哪款才正确和会不会下载到盗版......
  • 【C++】高效数据操作神器:C++ STL 中 set 和 map 的完整指南
    个人主页:起名字真南的CSDN博客个人专栏:【数据结构初阶】......
  • 【C++】深入理解 C++ 中的继承进阶:多继承、菱形继承及其解决方案
    个人主页:起名字真南的CSDN博客个人专栏:【数据结构初阶】......
  • 打卡信奥刷题(286)用C++工具信奥P2240[普及组/提高] 【深基12.例1】部分背包问题
    【深基12.例1】部分背包问题题目描述阿里巴巴走进了装满宝藏的藏宝洞。藏宝洞里面有N(N≤100......
  • c++枪声问题
    题目描述大联欢的最后项目是小明和小李的射击比赛。比赛规则是这样的,每次两人同时射击,每个人有S枚子弹进行射击,第1秒两人同时打出第一枚子弹,以后的s-1子弹可以自己根据一定的间隔时间打出,设小明后面的子弹每隔t1秒打出一枚子弹,小李后面的子弹每隔t2秒打出一枚子弹,......
  • C++——类和对象(上)
    前言C语言大家都不陌生,它的最大特点是面向过程而C++是C语言的升级版,它由原来的面向过程转变为面向对象两者有什么区别呢?本次文章带你处认识C++中的面向对象面向对象和面向过程C语言面向过程面向过程就是关心各个步骤的交互,步骤的顺序影响结果C++面向对象面向对象就......
  • C++四级抽测题目(答案+题目)
    今天我给大家出一套C++四级考题限时1小时,大家加油!!!题目1:改变二维数组题目描述输入一个n行m列的二维数组,把它的奇数和偶数行互换后输出。(如果n为奇数,最后的一行无需调换位置)n,m为不超过20的正整数。数组内的数据为不大于200的正整数。输入格式共n+1行......
  • C++三级抽测题目(答案+题目)
    今天我给大家出一套C++三级考题限时1.5小时,大家加油!!!题目1:回文数回文数题目描述若一个数(首位不为零)从左向右读与从右向左读都一样,我们就将其称之为回文数。例如:给定一个十进制数56,将56加65(即把56从右向左读),得到121是一个回文数。又如:数87:STEP1:87+78=165S......