C++怎么实现接口 C++使用虚基类定义标准接口【规范】

5次阅读

c++接口需用仅含public虚函数的抽象基类实现,virtual和=0不可省略,析构函数必须virtual;虚继承不适用于接口;推荐使用智能指针传递接口。

C++怎么实现接口 C++使用虚基类定义标准接口【规范】

为什么 C++ 没有 Interface 关键字,但你仍要模拟接口

因为 C++ 标准直到 C++20 才引入 concept(概念),而它不是接口的替代品;真正的运行时多态接口,必须靠纯虚函数类(abstract class)来承载。虚基类(virtual base class)本身和“定义接口”无关——它只解决菱形继承中的重复子对象问题,误用反而会让接口语义变模糊。

所以:你要的不是虚基类,是「只含纯虚函数的抽象基类」,且所有函数都应声明为 publicvirtual= 0,析构函数必须是 virtual

  • 不写 virtual 析构函数 → 派生类对象通过基类指针 delete 时,派生部分不析构,内存泄漏或未定义行为
  • 把函数写成 protectedprivate → 用户无法调用,违背接口“供外部使用”的本质
  • 在接口类里加数据成员或非纯虚函数 → 它就不再是纯粹的契约,而是具体实现的混杂体

class 声明接口时,哪些修饰符不能省

一个被当作接口的类,每个成员函数声明中,virtual= 0 是硬性要求;public 访问限定符也必须显式写出——C++ 默认是 private,漏写就等于把接口函数锁死了。

示例:

立即学习C++免费学习笔记(深入)”;

class Drawable { public:     virtual ~Drawable() = default; // 必须 virtual,即使 default     virtual void draw() = 0;       // virtual + = 0 缺一不可     virtual void resize(float scale) = 0; };
  • ~Drawable() 不写 virtual → 多态销毁失效
  • draw() 漏掉 virtual → 派生类重写后,通过基类指针调用仍走基类空实现(如果有)或报错
  • resize() 忘了 = 0 → 这个类不再是抽象类,用户能直接实例化,失去接口约束力

继承多个接口时,虚继承(virtual)要不要加

不需要。接口类之间无数据成员、无构造逻辑,彼此正交,多重继承它们不会引发二义性或重复子对象问题。加 virtual 反而增加虚表开销,且让继承关系图人为复杂化。

正确做法是普通 public 继承:

class Clickable : public virtual Drawable { /* ... */ }; // ❌ 错误:虚继承接口无意义
class Clickable : public Drawable, public Focusable { /* ... */ }; // ✅ 正确
  • 只有当两个父类共同继承自同一个有状态的基类(比如 Shapecolor 成员),才需要虚继承避免重复
  • 接口类(如 DrawableFocusable)不含数据、不参与构造,虚继承只拖慢编译和运行时查表
  • 若真出现函数名冲突(比如两个接口都有 handle()),说明设计有问题——接口职责应正交,冲突意味着抽象粒度错了

std::unique_ptr<interface></interface> 还是裸指针传接口

优先用 std::unique_ptr<drawable></drawable>std::shared_ptr<drawable></drawable>,而不是 Drawable*Drawable&。裸指针不表达所有权,调用方无法判断是否该释放、能否为空;引用又强制非空,灵活性差。

  • void render(std::unique_ptr<drawable> obj)</drawable> → 明确表示“我接管生命周期”,调用方必须 move 进来
  • void draw(const Drawable& obj) → 仅读取,不涉所有权,适合临时使用
  • void set_callback(Drawable* cb) → 危险!无法保证 cb 生命周期长于持有者,易悬空

真正难处理的是跨模块/跨 DLL 的接口传递:此时 RTTI 和虚表布局可能不一致,dynamic_cast 失效,建议用 PIMPL 或 C 风格函数指针封装,而非强依赖 C++ 虚函数机制。

text=ZqhQzanResources