继承抽象类时public与private继承的差异及测试困惑
Public vs Private Inheritance for Interface-only Base Classes in C++
核心结论
虽然特定测试场景下两种继承方式看起来能得到相同的虚函数调用结果,但二者存在本质差异,public继承才是符合你接口设计意图的正确选择。
为什么测试时看起来无差异?
虚函数的动态派发依赖于对象的实际类型和虚函数表(vtable),和继承的访问权限无关。只要派生类覆写了基类的虚函数,无论继承方式是public还是private,只要你能合法(或非法强制)获取到基类指针/引用,调用虚函数时都会触发派生类的实现。
但要注意:private继承下,外部代码无法合法地将派生类对象隐式转换为基类类型——你能测试成功,大概率是用了强制类型转换(比如static_cast)或者在派生类内部/友元中进行了转换,这属于绕过访问权限的非常规操作。
两种继承方式的本质差异
1. Public继承:"is-a" 语义
public继承表达的是派生类是基类的一种的关系,完全匹配你把Placeable作为公共接口的设计目标:
- 外部代码可以自由地将
Box*/Box&隐式转换为Placeable*/Placeable&; - 任何接受
Placeable接口的函数,都能直接传入Box对象,比如:void render_placeable(const Placeable& obj) { obj.place_at(0, 0); } int main() { Box b; render_placeable(b); // 合法,public继承支持is-a关系 }
2. Private继承:"implements-in-terms-of" 语义
private继承表达的是派生类基于基类的实现来构建的关系,基类的接口对外是隐藏的:
- 外部代码无法将
Box*/Box&隐式转换为Placeable*/Placeable&,上面的render_placeable(b)会直接编译报错; - 只有
Box类内部或其友元能访问Placeable的成员,这完全违背了你把Placeable作为公共接口的初衷。
为什么不应该用private继承?
你的核心需求是让Placeable作为规定接口的抽象类,意味着所有实现类都应该对外暴露这个接口,让外部代码可以通过统一的Placeable接口操作不同的实现类。private继承会把这个接口隐藏起来,破坏了面向对象的多态设计,导致其他代码无法正常使用你的Box类作为Placeable接口的实现。
额外优化建议
你的Placeable类目前不是严格的抽象类——place_at有默认实现。如果要让它成为纯接口,应该把place_at声明为纯虚函数:
struct Placeable { virtual void place_at(int x, int y) const = 0; // 纯虚函数,强制派生类实现 virtual ~Placeable() = default; };
这样能确保所有派生类必须实现place_at,更符合接口的定义。
内容的提问来源于stack exchange,提问作者Christopher Miller
相关产品推荐
相关产品推荐

