C++ 中的接口:给厌倦了搜索的人的指南
\n\n如果你曾经用 Java 或 C# 写过代码,然后转向了 C++,那么你很可能问过自己:"这里的 interface 关键字在哪里?"。剧透:它不存在。在 C++ 中,接口是通过抽象类实现的。这不是一个缺陷,而是一个特性,它提供了令人难以置信的灵活性。
\n\n在 Stack Overflow 上,问题 "How do you declare an interface in C++?" 获得了数千次浏览。这意味着即使是经验丰富的开发人员有时也会感到困惑。今天,我们将一劳永逸地弄清楚如何在 C++ 中声明接口,让你的代码保持干净,让编译器满意。
\n\nC++ 中的接口是什么?(剧透:它是抽象类)
\n\n在经典的 OOP 中,接口是一种契约。实现接口的类必须提供其所有方法的实现。在 C++ 中,这个角色由抽象类扮演——一个包含至少一个纯虚函数(pure virtual function)的类。
\n\n记住主要规则:
\n- \n
- 接口 = 所有方法都是纯虚函数的类。 \n
- 抽象类 = 至少有一个方法是纯虚函数的类(但也可以有带实现的普通方法)。 \n
对于纯接口,我们使用第一种方案:没有逻辑,只有声明。
\n\n如何声明纯接口:基本语法
\n\n以下是一个几何图形接口的最小示例:
\n\nclass IShape {\npublic:\n virtual double area() const = 0; // 纯虚函数\n virtual double perimeter() const = 0; // 另一个纯虚函数\n virtual ~IShape() {} // 虚析构函数——必须要有!\n};\n\n注意声明末尾的 = 0。这就是“纯虚”的神奇标记。这样的方法在基类中没有主体(实现)。
关键点:
\n- \n
- 类名中的前缀 I 是接口的非正式约定(I = Interface)。可以不使用,但这有助于代码阅读。 \n
- 虚析构函数是必须的。如果你通过指向接口的指针删除对象,没有虚析构函数会导致未定义行为(也就是说,程序会崩溃)。 \n
- 所有方法都是 public 的——接口应该可以从外部调用。 \n
接口的实现:从理论到实践
\n\n现在,让我们编写一个实现我们接口 IShape 的类。假设它是一个圆:
class Circle : public IShape {\nprivate:\n double radius;\npublic:\n explicit Circle(double r) : radius(r) {}\n\n double area() const override {\n return 3.14159 * radius * radius;\n }\n\n double perimeter() const override {\n return 2 * 3.14159 * radius;\n }\n};\n\n关键字 override(C++11 及更高版本)不是必须的,但强烈建议使用。它强制编译器检查你是否确实重写了基类中的虚方法。方法名拼写错误?编译器会说:“错误!接口中没有这个方法”。
多态的实际应用
\n\n现在最有趣的部分来了——我们可以通过统一的接口处理不同的图形:
\n\nvoid printShapeInfo(const IShape& shape) {\n std::cout << "Area: " << shape.area() << "\\n";\n std::cout << "Perimeter: " << shape.perimeter() << "\\n";\n}\n\nint main() {\n Circle c(5.0);\n printShapeInfo(c); // 可以工作!\n return 0;\n}\n\n函数 printShapeInfo 接受一个对 IShape 的引用。它不在乎传递的是哪个具体对象——圆、正方形还是三角形。只要它们实现了接口,一切都能正常工作。这就是多态