📜  派生类中的虚函数(1)

📅  最后修改于: 2023-12-03 15:11:05.344000             🧑  作者: Mango

派生类中的虚函数

在C++中,派生类可以通过继承基类的函数和数据成员,从而避免了代码重复,提高了代码重用性。同时,派生类还可以重写基类中的虚函数,实现自己特定的功能逻辑。本文将介绍派生类中的虚函数。

什么是虚函数?

在C++中,虚函数是在基类中声明的函数,可以在派生类中被重写,且基类指针可以指向派生类对象并调用派生类的函数。虚函数的声明格式如下:

virtual 返回类型 函数名(参数列表) = 0;

其中,virtual是关键字,用来声明函数为虚函数。如果基类中声明了至少一个纯虚函数,那么这个基类就是抽象类,无法实例化。虚函数可以提供一个实现,也可以不提供实现。如果在函数声明后面加上= 0,那么这个函数就变成了纯虚函数。

派生类中的虚函数重写

派生类可以随意使用基类中的所有非私有成员,包括成员变量和成员函数。如果派生类需要重写一个虚函数,那么它必须按照以下规则重写:

  • 如果派生类不想改变基类中虚函数的行为,只需简单地将函数声明为虚函数即可。

  • 如果派生类想改变基类中虚函数的行为,那么需要重新实现该函数。派生类中的虚函数必须与基类中的虚函数具有相同的名称、参数列表和返回类型。

class Base {
public:
    virtual void myFunc() {
        std::cout << "Base Class!" << std::endl;
    }
};

class Derived: public Base {
public:
    void myFunc() { // 重写基类中的虚函数
        std::cout << "Derived Class!" << std::endl;
    }
};

int main() {
    Base *base = new Derived();
    base->myFunc(); // 输出 "Derived Class!"
    return 0; 
}
虚函数的覆盖

如果派生类中的虚函数与基类中的虚函数不具有相同的返回类型或参数列表,则不能称其为重写,而是称其为覆盖。当使用类的指针或引用调用虚函数时,实际调用的是对象的类型所对应的虚函数。如果基类中的虚函数被覆盖,那么通过基类指针或引用访问该函数时,实际调用的是派生类中的虚函数。

class Base {
public:
    virtual void myFunc() {
        std::cout << "Base Class!" << std::endl;
    }
};

class Derived: public Base {
public:
    void myFunc(int i) { // 覆盖基类中的虚函数
        std::cout << "Derived Class!" << std::endl;
    }
};

int main() {
    Base *base = new Derived();
    base->myFunc(); // 输出 "Base Class!"
    Derived *derived = new Derived();
    derived->myFunc(1); // 输出 "Derived Class!"
    return 0; 
}
虚析构器

如果一个类的析构函数是虚函数,那么在析构时就会按照对象所属的类和类型表(virtual table)找到正确的析构函数进行调用。当使用派生类对象删除基类指针时,如果基类中的析构函数不是虚函数,那么只会调用基类中的析构函数,不会调用派生类中的析构函数,这可能会导致内存泄漏。

class Base {
public:
    virtual ~Base() {
        std::cout << "Base Class!" << std::endl;
    }
};

class Derived: public Base {
public:
    ~Derived() { // 覆盖基类中的析构函数
        std::cout << "Derived Class!" << std::endl;
    }
};

int main() {
    Base *base = new Derived();
    delete base; // 输出 "Derived Class!" "Base Class!"
    return 0; 
}
总结
  • 虚函数是在基类中声明的,并可以在派生类中被重写或覆盖。

  • 派生类中重写虚函数的函数签名必须和基类中的虚函数相同。

  • 如果在类中声明了虚函数,则析构函数通常也应该声明为虚函数。

  • 当使用派生类对象删除基类指针时,如果基类中的析构函数不是虚函数,那么只会调用基类中的析构函数,不会调用派生类中的析构函数,这可能会导致内存泄漏。