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.只有类中的虚函数才能被声明为纯虚函数,普通成员函数和顶层函数均不能声明为纯虚函数

  • 69
    点赞
  • 315
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值