虚函数

  • 通过基类的指针删除派生类对象时,通常只调用基类的析构函数
    • 但是删除一个攀升类对象时,应该先调用派生类的析构函数,然后调用基类的
  • 解决办法:把基类的析构函数声明为virtual
    • 派生类的析构函数可以virtual不进行声明
    • 通过基类指针删除派生类对象时,首先调用派生类的析构函数,然后调用基类的析构函数
  • 一般来说,一个类如果定义了虚函数,则应该将析构函数也定义成虚函数。或者,一个类打算作为基类使用,也应该将析构函数定义成虚函数
  • NOTICE:不允许将构造函数声明为虚函数

    1. class son {
    2. public:
    3. ~son() {cout << "bye feo son" << endl;}
    4. };
    5. class grandson:public:son {
    6. public:
    7. ~grandson() {cout << "bye from grandson" << endl;}
    8. };
    9. main() {
    10. son * pson;
    11. pson = new grandson();
    12. delete pson;
    13. } // 输出 bye from son 没有执行 grandson::~grandson()!!!

    纯虚函数和抽象类

  • 纯虚函数:没有函数体的虚函数

    1. class A {
    2. private: int a;
    3. public:
    4. virtual void Print() = 0; // 纯虚函数
    5. void fun() { cout << "fun"; }
    6. };
  • 包含纯虚函数的类叫做抽象类

    • 抽象类只能作为基类来派生新类使用,不能创建抽象类的对象
    • 抽象类的指针和引用可以指向抽象类派生出的类的对象

A a; // wrong A是抽象类,不能创建对象
A * pa; // ok 可以定义指针和引用
pa = new A; // wrong 不能创建对象

  • 抽象类的成员函数内可以调用纯虚函数,但是在构造函数或析构函数内部不能调用纯虚函数
  • 如果一个类从抽象类派生而来,那么当且仅当它实现了基类所有纯虚函数,它才能成为非抽象类
    1. class A {
    2. public:
    3. virtual void f() = 0; // 纯虚函数
    4. void g() { this->f(); } // ok 多态,一定是派生类的f()
    5. A() { f(); // 错 不是多态,函数体都没有 }
    6. };
    7. class B:public A {
    8. public:
    9. void f() { cout << "B:f()" << endl; }
    10. };
    11. main() {
    12. B b;
    13. b.g(); // 输出B:f()
    14. }