目录
摘要
虚函数(Virtual Functions)
定义
用法
纯虚函数(Pure Virtual Functions)
定义
用法
需要避开的坑
总结
摘要
在C++中,我们经常会在开发中使用到虚函数(Virtual Functions)和纯虚函数(Pure Virtual Functions),而它们都是面向对象编程的重要概念,它们允许实现多态性,但也有一些我们在实际编程时需要注意的坑。
虚函数(Virtual Functions)
定义
虚函数是在基类中声明并且使用 `virtual` 关键字修饰的成员函数。它可以被派生类重写,以实现运行时多态性。
class Base {
public:virtual void show() {std::cout << "Base class show function" << std::endl;}
};
用法
1. 基类中声明虚函数:
在基类中声明虚函数,以便派生类可以重写它。
class Base {
public:virtual void show() {std::cout << "Base class show function" << std::endl;}
};
2. 派生类中重写虚函数:
在派生类中使用相同的函数签名重写虚函数。
class Derived : public Base {
public:void show() override {std::cout << "Derived class show function" << std::endl;}
};
3. 使用基类指针或引用调用虚函数:
通过基类指针或引用调用虚函数时,会根据对象的实际类型调用相应的函数。
void describe(const Base &obj) {obj.show();
}int main() {Base baseObj;Derived derivedObj;describe(baseObj); // 输出 "Base class show function"describe(derivedObj); // 输出 "Derived class show function"return 0;
}
纯虚函数(Pure Virtual Functions)
定义
纯虚函数是在基类中声明但没有实现的虚函数,通过在函数声明末尾使用 `= 0` 来声明。
class AbstractBase {
public:virtual void show() = 0; // 纯虚函数
};
用法
1. 作为接口:
纯虚函数通常用于定义接口,要求派生类必须提供实现。
class AbstractBase {
public:virtual void show() = 0; // 纯虚函数,定义接口
};class ConcreteDerived : public AbstractBase {
public:void show() override {std::cout << "ConcreteDerived class show function" << std::endl;}
};
2. 抽象基类:
包含纯虚函数的类称为抽象基类,不能实例化,只能用作派生类的接口。
AbstractBase *ptr = new ConcreteDerived();
ptr->show(); // 输出 "ConcreteDerived class show function"
需要避开的坑
1. 未实现纯虚函数的派生类:
如果派生类没有实现基类中的所有纯虚函数,则该派生类仍然是抽象的,不能实例化。
2. 构造函数中调用虚函数:
在构造函数中调用虚函数,会导致基类的版本被调用,而不是派生类的版本。
3. 析构函数中调用虚函数:
在析构函数中调用虚函数,会导致派生类的版本被调用,但是可能会出现问题,因为在派生类对象的析构函数执行完毕后,其成员变量被销毁,再调用虚函数就可能会访问到已销毁的内存,导致未定义行为。
4. 虚函数的默认参数:
虚函数的默认参数是静态绑定的,不会根据运行时对象的实际类型而改变。
5. 虚函数与模板:
在模板类中使用虚函数可能会导致编译器难以生成有效的代码,因为模板的特化版本可能会对虚函数的实现产生影响。
#include <iostream>class AbstractBase {
public:virtual void show() = 0; // 纯虚函数virtual ~AbstractBase() {}
};class ConcreteDerived : public AbstractBase {
public:void show() override {std::cout << "ConcreteDerived class show function" << std::endl;}~ConcreteDerived() {std::cout << "ConcreteDerived destructor" << std::endl;}
};int main() {// AbstractBase *ptr = new AbstractBase(); // 错误:不能实例化抽象类AbstractBase *ptr = new ConcreteDerived();ptr->show(); // 输出 "ConcreteDerived class show function"delete ptr; // 输出 "ConcreteDerived destructor"return 0;
}
总结
1. 虚函数:
- 使用 `virtual` 关键字声明,允许在派生类中重写。
- 允许通过基类指针或引用调用派生类的方法。
2. 纯虚函数:
- 使用 `virtual` 关键字声明并在末尾添加 `= 0`,作为接口的一部分。
- 不能实例化,只能作为派生类的接口,派生类必须实现所有纯虚函数。
虚函数和纯虚函数在实际应用中广泛使用,且很多功能的继承和多态也离不开虚函数。需要我们注意到的是虚函数留下的坑,不然问题真的很难被发现。