[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;
    }
}
posted @ 2020-10-06 22:04  sinkinben  阅读(715)  评论(0编辑  收藏  举报