目录
前言
上一期我们学习了虚函数,本期我们学习一种特殊的虚函数,纯虚函数。
C++ 纯虚函数本质上与其他语言中的抽象方法或接口相同,基本上,纯虚函数允许我们在基类中定义一个没有实现的函数,然后强制子类去实现该函数。
我们可以看一下之前关于虚函数的例子。
你可以看到在 Animal类中有一个虚函数 GetName,然后我们在 Dog类中重写了那个函数,在基类中,这个 GetName 函数有函数体,意味着在某个类中重写它只是一个可选项,即使不重写它,仍然是可以调用它的。
然而在某些情况下,提供这种默认实现是没有意义的,实际上我们可能想要强制子类为特定的函数提供自己的定义,所以我们就有了纯虚函数。
01 纯虚函数
在面向对象编程中,创建一个类,只由未实现的方法组成,然后强制子类去实现它们的操作非常常见,这种类通常被称为接口。
因此,类中的接口只包含未实现的方法作为模板,由于这个接口实际上并不包含方法实现,我们实际上不可能实例化那个类,
让我们来看看这个在 Animol类中的 GetName 函数能不能搞成纯虚函数。
在上面的代码中,我们依然是将 GetName 函数定义为虚函数,但等于0使它成为一个纯虚函数,这意味着它必须在一个子类中实现,不然的话就会报错,给出的错误是:
不允许使用抽象类类型 "Animal" 的对象:C/C++(322)
29.cpp(22, 26): 函数 "Animal::getNmae" 是纯虚拟函数
首先,我们看一下 main 函数,可以看到现在不具有实例化 Animal类的能力,我们必须给它一个子类来实现这个函数。
使用 Dog 类。
你可以看到 Dog类是可以正常工作的。
这只是因为我们在 Dog类中实现了那个 GetName 函数,你可以试试注释掉 Dog中的 GetName 函数,你会发现 Dog也不能进行实例化了。
本质上,你只能在实现了所有这些纯虚函数之后,才能够实例化,或者在更上层次的类中完成。比如,Dog类是另一个类(Animal的子类)的子类,而这个类实现了 GetName 函数,那也是可以的。
02 纯虚函数必须被实现,才能创建这个类的实例。
让我们来看另外一个更好的例子。
在上面,我们创建一个新类,叫 Print,它只有这些内容,它会创建一个 public virtual 字符串函数,返回一个字符串,这个函数是纯虚函数。然后让 Animal实现print这个接口。
我们编写一个函数来打印这些类的类名,保证 obj 有GetName 函数。现在我们需要一个类型,可以提供 GetName 函数,这就是所谓的接口。
还有就是,我虽然把 Print 叫做接口 interface,但它其实只是一个类,所以还是叫 class 而 不是叫做 interface,因为它只不过有一个纯虚函数
现在所有的类都需要实现这个 GetCName 函数了。
我们无法实现实例化的问题已经解决了, 现在已经提供了那个功能。
可以看到,现在我们得到了正确的类名,所有的这些都来自一个 Print 函数。这个函数接受 Print 的指针 作为参数,它并不关心具体是什么类。
比如,我们可以创建一个完全不同的类,比如 cat,它是一个 Print类的子类,它必须有相应的函数。
我现在创建了一个全新的类,并且实现了 Print 这个接口,即实现了这个函数,像之前那样调用打印,你会看到结果是正常的。
03 测试代码
//此为本章的测试代码
#include <iostream>
class Print
{
public:
virtual std::string getName() = 0;
};
class Animal: public Print
{
public:
std::string getName()
{
return "Animal";
}
};
class Dog: public Animal
{
public:
std::string getName()
{
return "Dog";
};
};
class Cat:public Print
{
std::string getName()override{return "Cat";}
};
void print(Print* obj)
{
std::cout << obj->getName() << std::endl;
}
int main()
{
Animal *animal = new Animal;
Dog *dog = new Dog;
Cat *cat = new Cat;
print(cat);
print(animal);
print(dog);
return 0;
}
最后的话
这就是纯虚函数整个的工作原理,它知道任何 Printable 的东西,它们都有一个 GetClassName 函数去调用,如果你不实现这个函数,你就不能实例化这个类。这就是 C++ 中的接口,也就是 C++ 中的纯虚函数,它是非常有用的一个工具,可以被用在刚才的这个场景下,如果想要确保类都有一个特定的方法,那么可以将这个类作为参数放入一个通用的函数中,然后就可以调用这个方法或者做其他事情。