C++继承方式有哪些?
公有继承(public inheritance)
公有继承是最常用的继承方式,它直接表达了“is-a”的语义:派生类通常可以被当作基类来使用。基类的公有成员在派生类中依然保持公有访问权限,基类的保护成员在派生类中仍然保持保护访问权限,而基类的私有成员对派生类不可直接访问。通过公有继承,派生类可以复用基类的实现并向外部暴露基类的接口。随着继承关系的清晰,代码的可维护性和可扩展性也更易于提升。本文还会给出具体代码示例帮助理解。
在实际设计中,公有继承通常用于表示“is-a”关系,也就是说派生类在行为上应当被视为一个基类的特例。例如,若有一个基类 Shape,派生自 Circle、Rectangle 等,公开继承可以确保外部代码以 Shape 的形式使用子类型对象。
class Base {
public:int public_member;
protected:int protected_member;
private:int private_member;
};class Derived : public Base {
public:void access() {public_member = 1; // OK,公有成员仍然公开protected_member = 2; // OK,保护成员仍然受保护// private_member = 3; // 不可访问}
};
使用公有继承时应注意接口暴露,不要把基类的实现细节直接暴露给外部;同时如果基类包含不可访问的私有数据或复杂实现,派生类需要通过公开的成员函数来访问。
保护继承(protected inheritance)
在 保护继承 中,基类的公有成员和保护成员在派生类中都变为保护成员,外部代码无法直接访问派生对象的这些成员。只有派生类及其子类能够访问它们。这种方式通常用于实现复用而不希望将基类接口暴露给最终用户,保持更严格的接口控制。通过保护继承,可以让派生类内部及其后代仍然可用基类的实现细节,而外部使用者只能通过派生类提供的公开接口进行交互。
在实际场景中,保护继承适用于需要对外隐藏基类接口,只让后代类继续使用基类实现的情况,例如某些框架内部的实现细节复用但不希望 API 对象暴露相关方法。
class Base {
public:void f();
};
class DerivedProtected : protected Base {
public:void callF() { f(); } // OK,派生类内部可以调用
};
int main() {DerivedProtected d;// d.f(); // error,f 在 DerivedProtected 中是保护成员,外部不可访问
}
注意点:外部对象无法直接使用 Base 的公有成员,但派生类的成员函数仍然可以通过内部实现调用它们;如果需要让某些功能对外暴露,可以在派生类中提供公开的包装接口。
私有继承(private inheritance)
在 私有继承 中,基类的公有成员和保护成员在派生类中都变为私有成员,外部代码无法通过派生对象访问这些成员。此时,派生类的行为更多地体现为对基类实现的“重用”而非“is-a”关系。私有继承常用于实现复用、将基类的实现细节隐藏起来,同时通过派生类自身的接口为使用者提供有限功能。
为了在私有继承的基础上重新暴露某些基类成员,可以在派生类中使用 using 语句把基类成员重新设为公有或受保护的可访问性,实现在不改变对外接口的同时增加灵活性。这是一种常见的技巧,用于在防止滥用的同时提供需要的功能。
class Base {
public:void g();
};// 私有继承,外部不可直接访问 g
class DerivedPrivate : private Base {
public:using Base::g; // 将 g 重新公开
};
int main() {DerivedPrivate d;d.g(); // OK,因为使用了 using Base::g 将其暴露为公有
}
在选择私有继承时,要权衡“实现复用”和“对外接口控制”之间的取舍,通常只有在希望隐藏基类接口、但仍需要复用实现时才考虑使用私有继承。若目标是建立“is-a”关系,优先考虑公有继承;若目标是实现细节复用且不希望外部直接使用基类接口,才考虑私有继承,并结合 using 声明来按需暴露接口。



