虚函数
1 2 3 4 5 6
| class Animal { public: virtual void speak() { std::cout << "Animal speaks" << std::endl; } };
|
虚函数在基类中使用virtual
关键字声明成员函数,并允许子类重写该函数,以提供特定于子类的实现。通俗一点的讲:
- 如果子类没有重写该函数(即提供虚函数的实现),将会自动调用基类的缺省虚函数实现
- 如果子类重写了该函数,则调用重写后的实现
创建虚函数时,虚函数所在类在申请内存时会同时生成虚函数指针vptr
,该指针指向“虚函数表”vtbl
,表中数据为函数指针,存储了对应虚函数具体实现所对应的位置。
因此,虚函数的实现过程是:
- 通过对象内存中的虚函数指针
vptr
找到虚函数表vtbl
- 通过
vtbl
中的函数指针找到对应虚函数的实现区域并进行调用
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| #include <iostream>
class Animal { public: virtual void speak() { std::cout << "Animal speaks" << std::endl; } };
class Cat : public Animal { public: void speak() override { std::cout << "Meow" << std::endl; } };
class Dog : public Animal { public: void speak() override { std::cout << "Bark" << std::endl; } };
int main() { Animal* cat = new Cat(); cat->speak();
Animal* dog = new Dog(); dog->speak();
return 0; }
|
由于speak()
为虚函数,cat->speak()
会调用Cat::speak()
而非Animal::speak()
。因为虚函数是动态绑定(运行时多态)的,其根据对象的实际类型(Cat)来决定调用哪个版本的speak()
。
纯虚函数
1 2 3 4
| class Animal { public: virtual void isalive() = 0; };
|
在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象(“一个动物”是什么?)明显不合常理。而针对每种动物的方法又有所不同,此时需要使用多态特性,也就需要在基类中定义虚函数。
纯虚函数是在基类中声明的虚函数,它要求任何派生类都要定义自己的实现方法,以实现多态性。实现了纯虚函数的子类,该纯虚函数在子类中就变成了虚函数。
定义纯虚函数是为了实现一个接口,用来规范派生类的行为,也即规范继承这个类的程序员必须实现这个函数。派生类仅仅只是继承函数的接口。纯虚函数的意义在于,让所有的类对象(主要是派生类对象)都可以执行纯虚函数的动作,但基类无法为纯虚函数提供一个合理的缺省实现。所以类纯虚函数的声明就是在告诉子类的设计者,“你必须提供一个纯虚函数的实现,但我不知道你会怎样实现它”。对于动物这个基类而言,它必须是某一个种类,可以是鸟、狗、猫,但不能什么都不是。这个“种类”就可以作为纯虚函数在基类Animal
中声明。
注意:含有纯虚函数的类称之为抽象类,它不能生成对象(创建实例),只能创建它的派生类的实例。抽象类是一种特殊的类,它是为了抽象和设计的目的为建立的,它处于继承层次结构的较上层。抽象类的主要作用是将有关的操作作为结果接口组织在一个继承层次结构中,由它来为派生类提供一个公共的根,派生类将具体实现在其基类中作为接口的操作。
抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出。如果派生类中没有重新定义纯虚函数,而只是继承基类的纯虚函数,则这个派生类仍然还是一个抽象类。如果派生类中给出了基类纯虚函数的实现,则该派生类就不再是抽象类了,它是一个可以建立对象的具体的类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| #include <iostream>
class Animal { public: virtual void speak() { std::cout << "Animal speaks" << std::endl; }
virtual void isalive() = 0; };
class Cat : public Animal { public: void speak() override { std::cout << "Meow" << std::endl; }
void isalive() override { std::cout << "Cat is alive" << std::endl; } };
class Dog : public Animal { public: void speak() override { std::cout << "Bark" << std::endl; }
void isalive() override { std::cout << "Dog is alive" << std::endl; } };
int main() { Animal* cat = new Cat(); cat->speak(); cat->isalive();
Animal* dog = new Dog(); dog->speak(); dog->isalive();
return 0; }
|
如果这里使用Animal* animal = new Animal()
,编译器将报错,因为Animal
包含纯虚函数,已经是一个抽象类。