公有继承与私有继承的区别 一、核心概念对比 特性 公有继承(public) 私有继承(private) 关系描述 is-a(是一个) has-a(有一个)/ 用…来实现 接口继承 保留基类接口 隐藏基类接口 类型兼容 派生类可转换为基类 不能转换 代码复用方式 白盒复用(内部可见) 黑盒复用(内部隐藏) 访问权限 基类 public → 派生类 public 基类所有成员 → 派生类 private
二、公有继承(is-a 关系) 2.1 定义与特点 class Engine { public : void Start ( ) { cout<< "引擎启动" << endl; } } ; class Car : public Engine { // 公有继承:Car is-a Engine // Car 拥有 Engine 的所有接口 } ; int main ( ) { Car car; car. Start ( ) ; // 可以直接调用基类方法 return 0 ; } 2.2 类型兼容性 class Person { } ; class Student : public Person { } ; // 公有继承 int main ( ) { Student s; Person* p= & s; // 派生类指针可转换为基类指针 Person& r= s; // 派生类引用可转换为基类引用 return 0 ; } 三、私有继承(has-a 关系) 3.1 定义与特点 class Engine { private : int cylinderNum; public : Engine ( int n= 4 ) : cylinderNum ( n) { } void EnStart ( ) { cout<< "引擎启动" << endl; } } ; // 私有继承:Car has-a Engine(用 Engine 来实现 Car) class Car : private Engine { public : Car ( int n= 4 ) : Engine ( n) { } void Start ( ) { Engine :: EnStart ( ) ; // 内部可以调用基类方法 } } ; int main ( ) { Car car; car. Start ( ) ; // 通过 Car 的公有接口调用 // car.EnStart(); // 错误!外部不能直接访问基类方法 return 0 ; } 3.2 私有继承的内存布局 私有继承的内存布局 Car 对象 ----------------------------------------------------------- Engine 部分(基类子对象) cylinderNum (4B) EnStart() 方法 --------------------------- Car 部分(派生类新增) (可能有其他成员) --------------------------- 特点:基类部分存在,但外部不可见四、公有继承 与 私有继承 与 组合 4.1 三种方式对比 方式 关系 接口可见性 类型兼容 耦合度 公有继承 is-a 完全可见 支持 较高 私有继承 has-a 完全隐藏 不支持 较低 组合 has-a 完全隐藏 不支持 最低
4.2 组合示例(推荐替代私有继承) // 使用组合替代私有继承 class Engine { public : void EnStart ( ) { cout<< "引擎启动" << endl; } } ; class Car { private : Engine engine; // 组合:Car has-a Engine public : void Start ( ) { engine. EnStart ( ) ; // 委托给成员对象 } } ; int main ( ) { Car car; car. Start ( ) ; // 通过 Car 接口调用 // car.EnStart(); // 不能直接访问 return 0 ; } 4.3 何时使用私有继承? 场景 推荐方式 原因 需要访问基类的 protected 成员 私有继承 组合无法访问 protected 成员 需要重写基类的虚函数 私有继承 可以重写虚函数实现定制 不需要上述特性 组合 更简单,耦合度更低
// 需要重写虚函数时使用私有继承 class Base { public : virtual void func ( ) { cout<< "Base" << endl; } } ; class Derived : private Base { public : void func ( ) override { cout<< "Derived" << endl; } // 重写虚函数 void call ( ) { func ( ) ; } } ; 五、多重继承与组合的选择 5.1 不推荐的多重继承 class Door { public : void Open ( ) { cout<< "开门" << endl; } } ; class Engine { public : void Start ( ) { cout<< "启动引擎" << endl; } } ; // 不推荐:汽车有多个属性,不应该用多重继承 class Car : private Engine , private Door { public : void Start ( ) { Engine :: Start ( ) ; } void Open ( ) { Door :: Open ( ) ; } } ; 5.2 推荐的组合方式 // 推荐:使用组合,将多个属性作为成员 class Car { private : Engine engine; // 引擎 Door door; // 门 Wheel wheels[ 4 ] ; // 车轮 public : void Start ( ) { engine. Start ( ) ; } void OpenDoor ( ) { door. Open ( ) ; } } ; 六、继承与静态成员 6.1 场景一:基类和派生类共享同一个静态成员 class Person { public : static int num; // 静态成员,只有一份 } ; int Person:: num= 0 ; class Student : public Person { public : Student ( ) { cout<< "Create Student: " << ++ num<< endl; } ~ Student ( ) { cout<< "Destroy Student: " << num-- << endl; } } ; class Employee : public Person { public : Employee ( ) { cout<< "Create Employee: " << ++ num<< endl; } ~ Employee ( ) { cout<< "Destroy Employee: " << num-- << endl; } } ; int main ( ) { Student s1, s2; Employee e1, e2; // 所有对象共享同一个 num cout<< & s1. num<< endl; // 地址相同 cout<< & e1. num<< endl; // 地址相同 return 0 ; } 内存布局 :
共享静态成员的内存布局 ------------------------------------------------------ 数据区(.data) Person::num = 4 (所有 Person、Student、Employee 对象共享) ------------------------------------------------------ Student 对象1 Student 对象2 Employee 对象1 Person 部分 Person 部分 Person 部分 (无 num) (无 num) (无 num)6.2 场景二:基类和派生类各自有同名静态成员 class Person { public : static int num; // Person 的静态成员 } ; int Person:: num= 0 ; class Student : public Person { public : static int num; // Student 自己的静态成员(隐藏了 Person::num) } ; int Student:: num= 0 ; class Employee : public Person { public : static int num; // Employee 自己的静态成员 } ; int Employee:: num= 0 ; int main ( ) { Student s1, s2; Employee e1, e2; // 三个静态成员地址不同 cout<< & s1. num<< endl; // Student::num 的地址 cout<< & e1. num<< endl; // Employee::num 的地址 cout<< & Person:: num<< endl; // Person::num 的地址 // 访问方式 Student:: num= 10 ; // 访问 Student 的静态成员 Person:: num= 100 ; // 访问 Person 的静态成员 return 0 ; } 内存布局 :
各自独立静态成员的内存布局 数据区(.data) -------------------------------------- Person::num = 100 -------------------------------------- Student::num = 10 -------------------------------------- Employee::num = 0 -------------------------------------- 注意:三个静态成员相互独立,地址不同七、完整示例代码 # include <iostream> using namespace std; // ========== 公有继承示例 ========== class Animal { public : void eat ( ) { cout<< "动物进食" << endl; } } ; class Dog : public Animal { // 公有继承 public : void bark ( ) { cout<< "汪汪叫" << endl; } } ; // ========== 私有继承示例 ========== class Engine { private : int power; public : Engine ( int p= 100 ) : power ( p) { } void start ( ) { cout<< "引擎启动,功率=" << power<< endl; } } ; class Car : private Engine { // 私有继承 public : Car ( int p= 100 ) : Engine ( p) { } void drive ( ) { start ( ) ; // 内部可以调用基类方法 cout<< "汽车行驶" << endl; } } ; // ========== 组合示例(推荐) ========== class Computer { private : CPU cpu; // 组合 Memory mem; // 组合 public : void run ( ) { cpu. process ( ) ; mem. load ( ) ; } } ; // ========== 静态成员测试 ========== class Base { public : static int count; } ; int Base:: count= 0 ; class Derived1 : public Base { public : Derived1 ( ) { count++ ; } } ; class Derived2 : public Base { public : Derived2 ( ) { count++ ; } } ; class Derived3 : public Base { public : static int count; // 隐藏基类静态成员 Derived3 ( ) { count++ ; } } ; int Derived3:: count= 0 ; int main ( ) { cout<< "=== 公有继承 ===" << endl; Dog dog; dog. eat ( ) ; // 继承自 Animal dog. bark ( ) ; // Dog 自己的方法 cout<< "\n=== 私有继承 ===" << endl; Carcar ( 150 ) ; car. drive ( ) ; // car.start(); // 外部无法访问 cout<< "\n=== 共享静态成员 ===" << endl; Derived1 d1, d2; Derived2 d3; cout<< "Base::count = " << Base:: count<< endl; // 3 cout<< "\n=== 独立静态成员 ===" << endl; Derived3 d4, d5; cout<< "Derived3::count = " << Derived3:: count<< endl; // 2 cout<< "Base::count = " << Base:: count<< endl; // 3(不变) return 0 ; } 八、选择 需求场景 推荐方式 原因 表达"is-a"关系 公有继承 符合面向对象设计 需要重写虚函数 公有/私有继承 私有继承也可重写 只需要使用功能 组合 更低耦合 需要访问 protected 成员 继承(公有或私有) 组合无法访问 表达"has-a"关系 组合优先 更灵活,更安全 需要多态 公有继承 支持基类指针/引用
九、建议 优先使用组合而非私有继承 ,除非需要访问 protected 成员或重写虚函数公有继承用于表达真实的"is-a"关系 避免多重继承 ,优先使用组合静态成员在继承中的行为要清楚 :同名会隐藏,不同名则共享私有继承是"实现继承" ,不应暴露给用户