0Pricing
C++ Academy · 课时

静态多态

避免虚调用开销

静态多态 是 CoddyKit 上的免费 C++ Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C++ Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C++ Academy 课程共包含 4 节课。

两种多态

C++ 支持两种多态形式。

  • 动态多态:通过虚函数表在运行时解析虚函数
  • 静态多态:在编译期解析模板和 CRTP

静态多态以灵活性换取速度。

虚函数的代价

虚函数调用需要通过虚函数表进行间接跳转。编译器通常无法将其内联,从而阻碍许多优化。

#include <iostream>

struct Shape {
    virtual double area() const = 0;
    virtual ~Shape() = default;
};

struct Square : Shape {
    double s;
    Square(double x) : s(x) {}
    double area() const override { return s * s; }
};

int main() {
    Shape* p = new Square(3);
    std::cout << p->area() << "\n";
    delete p;
    return 0;
}

使用 CRTP 进行静态分派

使用 CRTP 时,基类通过编译期转换直接调用派生类的方法,因此编译器可以将所有内容内联。

#include <iostream>

template <typename T>
struct Shape {
    double area() const { return static_cast<const T*>(this)->area(); }
};

struct Square : Shape<Square> {
    double s;
    Square(double x) : s(x) {}
    double area() const { return s * s; }
};

int main() {
    Square sq(3);
    std::cout << sq.area() << "\n";
    return 0;
}

转发函数命名

当基类和派生类都定义了 area 时,请为基类的转发函数使用不同的名称,例如 compute,以便明确调用派生类的 implementation。

#include <iostream>

template <typename T>
struct Shape {
    double compute() const { return static_cast<const T*>(this)->area(); }
};

struct Circle : Shape<Circle> {
    double r;
    Circle(double x) : r(x) {}
    double area() const { return 3.14159 * r * r; }
};

int main() {
    Circle c(2);
    std::cout << c.compute() << "\n";
    return 0;
}

作为静态多态的模板

普通函数模板同样属于静态多态:只要类型提供所需的接口,相同的代码就能适用于任何类型(编译期的鸭子类型)。

#include <iostream>

struct Dog { void speak() const { std::cout << "Woof\n"; } };
struct Cat { void speak() const { std::cout << "Meow\n"; } };

template <typename Animal>
void make_speak(const Animal& a) { a.speak(); }

int main() {
    make_speak(Dog{});
    make_speak(Cat{});
    return 0;
}

类型已知时

静态多态仅在编译时已知具体类型时才能使用。如果您必须将不同类型的对象存储在同一个容器中,并在运行时选择行为,那么仍然需要虚函数。

不支持异构容器

您不能将 Shape<Square> 和 Shape<Circle> 作为同一个公共基类放入同一个 std::vector,因为它们是不相关的类型。这是静态多态的核心限制。

性能优势

通过内联分派调用,编译器可以折叠常量,并完全消除函数调用开销。在针对单一已知类型的紧凑循环中,静态多态的速度可能会显著更快。

#include <iostream>

template <typename T>
struct Op {
    int apply(int x) const { return static_cast<const T*>(this)->apply(x); }
};

struct Doubler : Op<Doubler> {
    int apply(int x) const { return x * 2; }
};

int main() {
    Doubler d;
    int sum = 0;
    for (int i = 0; i < 5; ++i) sum += d.apply(i);
    std::cout << sum << "\n";
    return 0;
}

组合使用两者

您可以在热点路径中使用 CRTP,并在需要类型擦除时使用一个轻量的虚包装器。这样既能在关键位置获得速度,也能在需要时保留灵活性。

静态接口检查

如果派生类忘记实现所需的方法,错误会在实例化基类转发函数时于编译时出现,而不是等到运行时才出现。

#include <iostream>

template <typename T>
struct Greeter {
    void greet() const { static_cast<const T*>(this)->hello(); }
};

struct English : Greeter<English> {
    void hello() const { std::cout << "Hello\n"; }
};

int main() {
    English{}.greet();
    return 0;
}

选择合适的工具

请在以下情况下使用静态多态:

  • 类型在编译时已知
  • 性能至关重要
  • 不需要公共的运行时基类型

否则,请优先使用虚函数,以获得更清晰的代码和更好的灵活性。

快速检查

请选择静态多态的关键权衡。

回顾

您比较了静态多态和动态多态。

  • 虚函数 = 运行时分派、虚函数表、异构容器
  • CRTP/模板 = 编译时分派、可内联、速度更快
  • 静态多态要求类型在编译时已知
  • 请根据灵活性与性能需求进行选择

常见问题解答

「静态多态」课时是免费的吗?

是的 — 「静态多态」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C++ Academy 课程的其余内容,请升级到 CoddyKit PRO。 C++ Academy 课程共包含 4 节课。

「静态多态」这节课中我会学到什么?

避免虚调用开销 你通过在浏览器中直接运行的动手代码来练习 C++ Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 C++ Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 C++ Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「静态多态」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 C++ Academy 课中编写并运行代码吗?

能。每节 C++ Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. CRTP 惯用法
  2. 静态多态
  3. 使用 CRTP 实现混入
  4. 何时使用 CRTP
← 返回 C++ Academy