[CPP] 虚函数与纯虚函数
本文对 C++ 中对虚函数与纯虚函数做一次简单的总结。
虚函数:通过 virtual
关键字修饰的函数,作用是允许用父类的指针来调用子类的这个函数。虚函数具有函数体,「虚」不代表它是未实现的,相反地,它是必须要有函数体。
纯虚函数:函数未实现(没有函数体)。定义纯虚函数是为了实现一个接口,起到一个规范的作用,规范继承这个类的程序员必须实现这个函数。
虚函数
先看一段代码:
class A
{
public:
virtual void say() { cout << "A" << endl; }
};
class B : public A
{
public:
// 此处的 virtual 可有可无
void say() { cout << "B" << endl; }
};
int main()
{
A *a = new B();
a->say();
}
// Output: B
上述代码展示的是 virtual
的基本用法,A
中定义一个虚函数,B
中重新实现了一遍,这也是所谓的重写(类似于 Java 的 Override
)。
PS:如果函数
A::say()
没有使用virtual
修饰,那么就叫 重定义 (Redefining) 。 那么在这种情况下,a->say()
输出A
.
上面这种通过 父类指针调用子类函数 的行为,也被叫做动态绑定(Dynamic Binding),或者动态联编(这个就不知道是谁的翻译,无语子)。动态绑定的含义是:一个类函数的调用并不是在编译时刻被确定的,而是在运行时刻被确定的。
至于怎么理解「动态」一词呢?
参考文章:https://www.cnblogs.com/lizhenghn/p/3657717.html
4 个相关概念:
- 静态类型:对象在声明时采用的类型,在编译期既已确定;
- 动态类型:通常是指一个指针或引用目前所指对象的类型,是在运行期决定的;
- 静态绑定:绑定的是静态类型,所对应的函数或属性依赖于对象的静态类型,发生在编译期;
- 动态绑定:绑定的是动态类型,所对应的函数或属性依赖于对象的动态类型,发生在运行期;
int main()
{
A *a = nullptr; // a 的静态类型为 A*
a = new A(); // a 的动态类型为 A*
a->say();
a = new B(); // a 的动态类型变为 B*
a->say();
}
也就是说,由于 父类指针可以指向子类 这个特性,对象指针在运行时可以有不同的动态类型的。
一句话总结:虚函数的调用取决于指向或者引用的对象的类型,而不是指针或者引用自身的类型。
纯虚函数
纯虚函数是在基类中声明的虚函数,它在基类中没有定义(即没有函数体),但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加 = 0
:
class A
{
public:
virtual void say() = 0;
};
引入纯虚函数/抽象类的原因是:实现多态。
具有纯虚函数的类,称为抽象类(Abstract Class),不能实例化。其作用类似于 Java 中的接口,每个子类都必须要实现这个纯虚函数。
class Animal { public: virtual void whoami() = 0; };
class Cat : public Animal { public: void whoami() { cout << "I am cat." << endl; } };
class Dog : public Animal { public: void whoami() { cout << "I am dog." << endl; } };
一些常见的结论:
- 纯虚函数使一个类变成抽象类
- 可以定义抽象类的指针或者引用
int main()
{
Cat cat;
Dog dog;
Animal *ptr = nullptr;
ptr = &cat, ptr->whoami();
ptr = &dog, ptr->whoami();
Animal &ref = dog;
dog.whoami();
}
- 如果我们不在派生类中覆盖纯虚函数,那么派生类也会变成抽象类。
- 抽象类可以有构造函数
最后,可以看一下 Java 中 interface
的用法,与之十分类似。
在 Animal.java
中定义接口:
package sin;
public interface Animal {
public void whoami();
public boolean canFly();
}
在 Cat.java
实现接口中定义的所有方法:
package sin;
public class Cat implements Animal {
public void whoami() {
System.out.println("I am cat.");
}
public boolean canFly() {
return false;
}
}