减少虚函数表带来的开销
- 使用非虚接口(NVI)惯用法:
- 思路:通过一个非虚的公有成员函数来调用一个虚的私有成员函数。这样,公有接口保持稳定,而私有虚函数可以在子类中被重写。外部调用始终通过非虚函数,避免了直接调用虚函数的开销。
- 代码示例:
class Base {
public:
void doWork() {
// 这里可以添加一些通用的前置处理
doWorkImpl();
// 这里可以添加一些通用的后置处理
}
private:
virtual void doWorkImpl() {
// 默认实现
std::cout << "Base::doWorkImpl" << std::endl;
}
};
class Derived : public Base {
private:
void doWorkImpl() override {
std::cout << "Derived::doWorkImpl" << std::endl;
}
};
- 使用静态多态(模板):
- 思路:利用模板在编译期确定类型,从而避免运行时虚函数表的开销。这在一些特定场景下,比如算法的实现对于不同类型有相同的逻辑时非常有用。
- 代码示例:
template <typename T>
class StaticPolymorphism {
public:
void doWork() {
T* derived = static_cast<T*>(this);
derived->doWorkImpl();
}
};
class StaticBase : public StaticPolymorphism<StaticBase> {
public:
void doWorkImpl() {
std::cout << "StaticBase::doWorkImpl" << std::endl;
}
};
class StaticDerived : public StaticBase {
public:
void doWorkImpl() {
std::cout << "StaticDerived::doWorkImpl" << std::endl;
}
};
避免不必要的动态类型转换
- 使用虚函数而不是类型转换:
- 思路:在设计良好的继承体系中,尽量通过虚函数实现不同类型对象的差异化行为,而不是通过
dynamic_cast
进行类型判断后执行不同逻辑。
- 代码示例:
class Shape {
public:
virtual double area() const = 0;
};
class Circle : public Shape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double area() const override {
return 3.14159 * radius * radius;
}
};
class Rectangle : public Shape {
private:
double width;
double height;
public:
Rectangle(double w, double h) : width(w), height(h) {}
double area() const override {
return width * height;
}
};
// 正确做法,通过虚函数实现
void printArea(const Shape& shape) {
std::cout << "Area: " << shape.area() << std::endl;
}
- 在必要时使用
static_cast
替代dynamic_cast
:
- 思路:如果能在编译期确定类型转换的安全性,使用
static_cast
。dynamic_cast
用于运行时类型检查,开销较大。
- 代码示例:
Shape* shape = new Circle(5);
Circle* circle = static_cast<Circle*>(shape);
// 如果能确保shape指向的是Circle对象,用static_cast更高效