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

C++ 抽象类

抽象类(接口)

接口描述了类的行为和功能,而无需完成类的特定实现

C++接口时通过抽象类实现的,设计抽象类的目的,是为了给其他类提供一个可以继承的适当的基类.抽象类本类不能被用于实例化对象,只能作为接口使用

注意:
如果试图实例化一个抽象类的对象,会导致编译错误
因此,如果一个抽象类的派生类需要被实例化(建立对象),则必须对每个继承来的纯虚函数进行函数体实现.
如果没有在派生类中重写所有纯虚函数,就尝试实例化派生类的对象,也会导致编译错误,这是因为如果派生类没有实现父类的纯虚函数,则派生类变为抽象类

抽象类基类为派生自抽象基类的派生类提供了约束条件,即派生类必须要实现继承自抽象基类中的纯虚函数,否则此派生类不可进行实例化,且派生类将继承为抽象派生类


抽象类与纯虚函数(抽象方法)

纯虚函数是一个在 基类中声明的虚函数,它在该基类中没有定义具体的函数体(操作内容),要求派生类根据实际需要定义自己的版本,设计多层次类继承关系时用到。把某个方法声明为一个抽象方法等于告诉编译器,这个方法必不可少,但目前在基类中还不能为它提供实现

纯虚函数的标准格式:

virtual 函数类型 函数名(参数表)=0;

class Pet
{
public:
virtual void func()=0;//这便是声明了一个纯虚函数 也就是在虚函数尾部加上" =0 " 一个虚函数便被声明成为了一个纯虚函数
// 等于0表示该函数仅声明而没有函数体
};

🌟注意:一旦类中有了纯虚函数,这个类便被称为 抽象类,且此类不可被实例化(不可建立类对象实例)
例如:

int main()
{
Pet pet;//报错!带有纯虚函数的类称为抽象类,不可实例化
}

抽象类
抽象类只能作为基类使用,无法定义抽象类对象实例,这是因为抽象类中包含了没有定义的纯虚函数,在C++中,我们把只能用于被继承而不能直接创建对象的类称之为抽象类,这种基类不能直接生成对象,而只有被继承后,并重写其虚函数后,才能使用

当抽象类的派生类实现了继承而来的纯虚函数后,才能实例化对象

之所以要存在抽象类,最主要是因为它具有不确定因素,我们把那些类中的确存在,但是在父类中无法确定具体实现的成员函数称为虚函数。虚函数是一种特殊的函数,在抽象类中只有声明,没有具体的定义

抽象类和纯虚函数的关系
抽象类中至少存在一个纯虚函数,存在纯虚函数的类一定是抽象类,存在纯虚函数是成为抽象类的充要条件


为什么需要一个抽象类

让我们借助现实生活中的例子来理解这一点。让我们说我们有一个基类Animal,它会睡觉,发出声音等等。现在我只考虑这两个行为并创建一个具有两个函数sleep()和sound()的基类Animal

此时我们有猫和狗两种动物需要被赋予发出声音的属性,我们知道动物的声音是不同的,猫说“喵”,狗说“汪”,那么我在Animal类中为函数sound()提供了什么实现?这样做的唯一和正确的方法是使这个函数纯粹抽象,这样我就不需要在Animal类中给出实现但是所有继承Animal的类必须为此函数提供实现。这样我确保所有动物都有声音,而且它们有独特的声音

程序示例:

#include <iostream>

using namespace std;

//todonew与delete动态分配内存,与用指针调用对象 通过对象的不同调用不同的同名虚函数
class Pet//声明纯虚函数sound 后Pet类变为抽象类(接口)
{
public:
	Pet(string thename);
	void sleep();
	virtual void sound()=0;//声明纯虚函数sound  (并未进行函数实现 函数实现放在派生类中)
	
	//注意:					   
    //todo1.继承自抽象基类Pet的子类必须全部实现基类中的所有纯虚函数
	//todo2.抽象基类Pet不可进行实例化

protected:
	string name;
};

class Cat :public Pet
{
public:
	Cat(string thename);
	void climb();
	void sound();
};

class Dog :public Pet
{
public:
	Dog(string thename);
	void jump();
	void sound();
};


Pet::Pet(string thename)//todo基类构造器(抽象类也有构造函数)
{
	name = thename;
}

void Pet::sleep()
{
	cout << name << "正在睡大觉\n";
}

void Pet::sound()
{
	cout << name << "动物发声\n";
}

Cat::Cat(string thename) :Pet(thename)//派生类Cat构造函数
{
}

void Cat::climb()
{
	cout << name << "正在爬树\n";
}

void  Cat::sound()//派生类虚函数
{

//	Pet::sound();//todo如果需要调用基类中的play()函数  在原本的play()函数的基础上加上覆盖上的子类play()函数
	cout << name << "喵喵喵!\n";
}


Dog::Dog(string thename) :Pet(thename)//派生类Dog构造函数
{
}

void Dog::jump()
{
	cout << name << "跳过了栅栏\n";
}

void Dog::sound()//派生类虚函数
{
//	Pet::sound();
	cout << name << "汪汪汪!\n";
}

void func(Pet* x)
{	
	x->sound();
}


int main()
{
//	Pet pet;//todo用带有抽象方法(纯虚函数)的抽象类Pet无法实例化对象

	//todo创建指向子类实例的基类指针和引用来调用纯虚函数
	Pet* cat = new Cat("猫");
	Pet* dog = new Dog("狗");

	//todo创建对象实例来调用纯虚函数
	Cat cat2("对象实例调用 猫");
	cat2.sound();

	func(cat);
	func(dog);
	return 0;
}

本例中定义了三个类,它们的继承关系为:Animal-->CatAnimal-->Dog
Animal是一个抽象类,也是最顶层的基类,在 Animal类中定义了一个纯虚函数sound(),在Cat类中,实现了sound()函数。所谓实现,就是定义了纯虚函数的函数体,抽象类Animal虽然不能实例化,但它为派生类提供了约束条件,派生类必须要实现这个函数,完成动物发声功能,否则就不能对派生类进行实例化

在实际开发中,你可以定义一个抽象基类,只完成部分功能,未完成的功能交给派生类去实现(谁派生谁实现). 这部分未完成的功能,往往是基类不需要的,或者在基类中无法实现的,虽然抽象基类没有完成,但是却强制要求派生类完成,这就是抽象基类的“霸王条款”
运行结果:
在这里插入图片描述


总结:🎯

1.我们已经看到任何具有纯虚函数的类都是抽象类
2.抽象类基类不可建立实例
3.抽象类派生出的子类需将继承的纯虚函数全部进行实例化,才能建立其实例
4.抽象类可以有构造函数
5.如果派生类没有实现父类的纯虚函数,则派生类变为抽象类,即不可建立其实例
6.抽象基类除了约束派生类的功能,还可以实现多态,可以创建指向子类的实例的抽象基类的指针和引用
7.只有类中的虚函数才能被声明为纯虚函数,普通成员函数和顶层函数均不能声明为纯虚函数

相关文章:

  • C++ 运算符重载
  • 购买Microsoft Technet订阅,免费获得微软几乎所有的产品序列号“用于评估”,...
  • C++ 操作符重载
  • 客户端调用webservice的两种方式
  • C++ 多继承
  • redmine 主题thems-默认主题
  • C++ 虚继承
  • C++ 错误处理和调试(编写代码前的准备工作)
  • 将整型数字转换为大写汉字的自定义函数,如转换为'壹贰
  • C++ assert函数与捕获异常
  • WCF开发日志 -- OEA里面的WCF设计
  • C++ 内存分配
  • C++ 动态内存管理
  • IOS学习资源
  • C++ 从函数或方法返回动态内存(函数指针与指针函数)
  • 《Java8实战》-第四章读书笔记(引入流Stream)
  • iOS帅气加载动画、通知视图、红包助手、引导页、导航栏、朋友圈、小游戏等效果源码...
  • Java 9 被无情抛弃,Java 8 直接升级到 Java 10!!
  • js操作时间(持续更新)
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • Vue 2.3、2.4 知识点小结
  • VUE es6技巧写法(持续更新中~~~)
  • Web设计流程优化:网页效果图设计新思路
  • WordPress 获取当前文章下的所有附件/获取指定ID文章的附件(图片、文件、视频)...
  • 关于for循环的简单归纳
  • 基于Android乐音识别(2)
  • 买一台 iPhone X,还是创建一家未来的独角兽?
  • 配置 PM2 实现代码自动发布
  • 普通函数和构造函数的区别
  • 实现菜单下拉伸展折叠效果demo
  • 数组大概知多少
  • 主流的CSS水平和垂直居中技术大全
  • 自动记录MySQL慢查询快照脚本
  • 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署
  • 选择阿里云数据库HBase版十大理由
  • ​渐进式Web应用PWA的未来
  • (二)linux使用docker容器运行mysql
  • (二)pulsar安装在独立的docker中,python测试
  • (附源码)基于ssm的模具配件账单管理系统 毕业设计 081848
  • (南京观海微电子)——I3C协议介绍
  • (四)模仿学习-完成后台管理页面查询
  • (五)MySQL的备份及恢复
  • .net 简单实现MD5
  • .net 无限分类
  • .net访问oracle数据库性能问题
  • .Net中的集合
  • @Pointcut 使用
  • [2016.7.Test1] T1 三进制异或
  • [Android] Android ActivityManager
  • [c#基础]值类型和引用类型的Equals,==的区别
  • [Docker]三.Docker 部署nginx,以及映射端口,挂载数据卷
  • [Linux]Ubuntu noVNC使用
  • [Linux]使用CentOS镜像与rpm来安装虚拟机软件
  • [nginx] LEMP 架构随笔
  • [one_demo_11]二分查找法