C++笔记 - 虚函数

371 阅读3分钟

虚函数是实现多态特性的重要工具。下面看一个例子体会一下:

class base {
  public:
    virtual void show() { std::cout << "base" << std::endl; };
    virtual ~base() { std::cout << "base destruct" << std::endl; }
};

class derive : public base {
  public:
    void show() { std::cout << "derive" << std::endl; };
    ~derive() { std::cout << "derive destruct" << std::endl; }
};

int main(void) {
    derive der;
    base *d = &der;
    d->show();
    std::cout << "exit" << std::endl;
}

输出:

derive
exit
derive destruct
base destruct

通过基类的指针指向派生类的地址,在调用基类对象时,动态选择其真实类型关联的函数,从而实现多态特性

  • 构造函数为什么不能是虚函数?

    虚函数的主要目的是通过基类的指针或引用,调用派生类关联的函数,实现多态特性。而构造函数是在初始化一个对象时调用的,派生类此时并没有完全构造出来(调用构造函数前),也没有与对应的基类指针或引用关联起来,没有办法使用多态特性。构造函数是虚函数没有必要。

  • 虚构函数为什么可以是虚函数?

    因为释放堆上内存时可能通过基类的指针进行delete,如果析构函数不是虚函数,则会调用基类的析构函数,派生类就得不到正确的销毁,造成内存泄露。一般在使用多态特性时,基类的析构函数都应是虚函数

  • 虚函数的默认参数是静态绑定的。即,通过基类的指针访问派生类时,传入的是基类的默认参数,而不是派生类的默认参数。

  • 虚函数可以是私有的,只要在动态绑定时能绑定即可。例如:基类的虚函数是私有的,派生类的虚函数是公有的,则通过基类的指针或引用访问派生类的方法时,并不会存在找不到函数的错误。

纯虚函数

纯虚函数简单来说就是没有定义虚函数。写法就是在虚函数后添加= 0修饰。下面看一个例子:

class base {
  public:
    virtual void show() = 0;
    virtual ~base() { std::cout << "base destruct" << std::endl; }
};

class derive : public base {
  public:
    void show() { std::cout << "derive" << std::endl; };
    ~derive() { std::cout << "derive destruct" << std::endl; }
};

int main(void) {
    derive der;
    base *d = &der;
    d->show();
    std::cout << "exit" << std::endl;
}

纯虚函数有什么用呢?纯虚函数一般用于实现抽象类。那什么是抽象类呢?抽象类简单来说就是接口,通过定义一些共同行为的集合,对派生类进行约束。当然抽象类也可以有数据成员。那抽象类有什么特点呢?

  • 抽象类不能实例化,一般使用指针或引用实现多态特性。
  • 只要有一个纯虚函数,该类就是抽象类。
  • 抽象类的所有虚函数(析构函数除外),必须在派生类中实现,才能成为非抽象类,否则派生类也是抽象类
  • 抽象类可以有构造函数。抽象类的构造函数一般被派生类调用,初始化抽象类的数据成员。