【C++ | 抽象类】纯虚函数 和 抽象基类,为什么需要抽象基类

wkd_007 2024-07-26 14:05:02 阅读 81

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀

🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭

🤣本文内容🤣:🍭介绍 抽象基类 和 纯虚函数 🍭

😎金句分享😎:🍭你不能选择最好的,但最好的会来选择你——泰戈尔🍭

⏰发布时间⏰:

本文未经允许,不得转发!!!

目录

🎄一、概述🎄二、纯虚函数🎄三、抽象基类🎄四、为什么需要抽象类🎄五、总结


在这里插入图片描述

🎄一、概述

在生活中,有时会使用到比较抽象的词语,例如下面这个场景:

<code>A:帮我把那个“东西”拿过来。

B:你是说,桌面的那个杯子吗?

A:是的。

这个场景中的“东西”就属于抽象的词语。而在C++编程中,有时也会出现这样抽象的语法。例如,我们可能会定义一个“动物”基类,但代码中不会定义一个“动物”的实例,而是将“动物”基类派生出“狗”、“猫”等派生类,再使用派生类去实例化。

本文主要介绍下面几个内容:

1、什么是纯虚函数

2、什么是抽象基类?

3、为什么需要抽象基类?


在这里插入图片描述

🎄二、纯虚函数

上篇文章:【C++ | 虚函数】虚函数详解 及 例子代码演示(包含虚函数使用、动态绑定、虚函数表、虚表指针),我们详细地介绍了虚函数以及C++怎样实现虚函数的,但有一个也是很重要的知识点没在上篇文章介绍,这个知识点就是 纯虚函数

纯虚函数:本质上也是虚函数,在虚函数的函数原型的分号前加上<code>=0就可以将一个虚函数说明为纯虚函数。

下面代码声明了一个虚函数 eat:

virtual void eat() =0;

纯虚函数的特点:

1、纯虚函数声明就是在函数原型分号前加=0

2、纯虚函数允许只有声明,没有定义(实现);

3、纯虚函数可以没有定义,也可以有定义,但纯虚函数的定义只能在类外。

4、只有虚函数可以加=0,非虚函数加=0会编译报错:error: initializer specified for non-virtual method

🌰举例子

// g++ 24_pure_virtual.cpp

#include <iostream>

using namespace std;

class CAnimal{

public:

// 1、纯虚函数声明就是在函数原型分号前加`=0`

// 2、纯虚函数允许只有声明,没有定义(实现);

virtual void eat() =0;

virtual void run() =0;

};

// 3、纯虚函数的定义只能在类外。

inline void CAnimal::run()

{

cout << "Animal run" << endl;

}

int main ()

{

}


在这里插入图片描述

🎄三、抽象基类

抽象基类:英文名称是<code>abstract base class,缩写为ABC。抽象基类是包含了至少一个纯虚函数的基类,在代码中只能用来做基类,不能用来实例化对象。

抽象基类虽然不能实例化对象,但可以定义抽象基类的指针或引用,以此来管理其派生类对象。

抽象基类的特点:

1、抽象基类至少有一个纯虚函数;

2、抽象基类不能创建对象;

3、抽象基类要求派生类必须重写纯虚函数,以此来规范派生类的接口(函数);

4、派生类必须重写抽象基类中的所有纯虚函数,否则也是抽象类,无法实例化对象。

5、抽象基类的纯虚函数一般没有定义,即使有定义也没法创建对象去调用。

🌰举例子

// g++ 24_pure_virtual2.cpp

#include <iostream>

using namespace std;

class CAnimal{

public:

virtual void eat() =0;// 纯虚函数,本类是抽象基类

virtual void run() =0;

};

class CCat : public CAnimal{

public:

// 没有重写基类的所有纯虚函数,本类是抽象类

virtual void eat(){

cout << "cat eat" << endl;

}

};

class CDog : public CAnimal{

public:

// 重写基类的所有纯虚函数,本类不是抽象类

virtual void eat(){

cout << "dog eat" << endl;

}

virtual void run(){

cout << "dog run" << endl;

}

};

// 纯虚函数的定义只能在类外。

inline void CAnimal::run()

{

cout << "Animal run" << endl;

}

int main ()

{

//CAnimal animal;// 报错:error: cannot declare variable ‘animal’ to be of abstract type ‘CAnimal’

//CCat cat;// 报错:error: cannot declare variable ‘cat’ to be of abstract type ‘CCat’

CDog dog;

CAnimal *pAnimal = &dog;

pAnimal->run();

pAnimal->CAnimal::run(); // 指定调用基类的run

}

运行结果:

在这里插入图片描述


在这里插入图片描述

🎄四、为什么需要抽象类

之前一直在想,很多代码明明使用普通类也可以,为什么需要定义抽象类呢?

需要定义抽象基类的几个原因:

1、将接口与实现分离。接口是软件产品最有价值的资源,设计接口比实现接口需要耗费更昂贵的成本。因此,要将接口保护起来,以免在针对客户需求修改实现的时候,程序员不小心把接口破坏掉。

我们将一些必要的接口(函数)定义在抽象类,要求派生类必须按照这个格式去重写,如果不重写就休想创建对象。也就是说,抽象类将具体实现延迟到子类中,提供代码重用性和一定程度的设计约束。2、避免某些基类被实例化是不合理的。例如“形状”这个基类,被实例化之后反而会让人相当费解,所以干脆将“形状”这个类定义为抽象类,由它派生出正方形,三角形等子类。3、提示作用,当我们看到抽象基类就会知道其派生类一定会重写纯虚函数,而且也会去找抽象类的指针或引用,看看是否实现多态。其次,看到抽象类也知道它不会定义对象,也就不会去代码里找抽象类的对象了。


在这里插入图片描述

🎄五、总结

本文介绍了C++的纯虚函数、抽象基类,以及怎样使用它们,最后介绍了需要抽象基类的原因。

在这里插入图片描述

如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

参考:

《C++ primer》

《C++ primer plus》

C++为什么要定义抽象基类?

为什么需要抽象类和接口?

为什么使用抽象类?有什么好处?



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。