欢迎光临
我们一直在努力

继承方式:public、private 与 protected 继承

继承方式:public、private 与 protected 继承

在C++面向对象编程中,继承是实现代码复用、构建层次化类结构的核心机制,而继承方式则是继承体系中“权限控制”的关键——它决定了父类的public、private、protected成员,在子类中会拥有怎样的访问权限,以及子类对象、类外代码能否访问这些继承而来的成员。

上一篇博客我们讲解了继承的核心概念:父类与子类的“is-a”关系、构造/析构函数的执行顺序,以及单继承、多继承的基本用法。其中我们留下了一个关键疑问:为什么父类的成员有时子类能直接访问,有时却不能?为什么子类对象能调用父类的某些成员函数,有时又会编译报错?答案就藏在三种继承方式中。

C++提供了三种核心继承方式:public(公有继承)、private(私有继承)、protected(保护继承)。它们的核心区别不在于“是否继承父类成员”——实际上,无论哪种继承方式,子类都会继承父类的所有成员(包括public、private、protected成员),区别在于继承后,父类成员在子类中的访问权限会被重新限定。

很多初学者在学习继承方式时,最容易陷入两个核心误区:一是混淆“继承成员”和“访问权限”(误以为private继承不继承父类成员),二是不清楚三种继承方式的适用场景,滥用继承方式导致代码冗余、权限混乱。本文将从“访问权限基础”切入,逐一拆解三种继承方式的规则、实战用法,对比三者的核心差异,结合案例规避高频误区,同时衔接前序知识点(父类子类关系、构造析构顺序),帮你彻底搞懂三种继承方式的本质,学会在实际开发中灵活选择合适的继承方式,完善C++类与对象的知识体系,为后续学习多态、虚函数打下坚实基础。

核心前提回顾:1. 类的访问权限优先级:private < protected < public(private仅类内可访问,protected类内、子类可访问,public类内、类外、子类均可访问);2. 子类会继承父类的所有成员,但父类的private成员无论哪种继承方式,子类都无法直接访问(仅能通过父类public/protected接口间接访问);3. 继承的核心是“is-a”关系,继承方式仅控制权限,不改变继承本质。

一、先明确:继承方式的核心作用(为什么需要三种继承方式?)

在讲解具体的继承方式之前,我们先通过一个简单场景,搞清楚继承方式的核心价值——如果只有一种继承方式,会遇到什么问题?三种继承方式能帮我们解决什么?

场景:定义父类Person(封装name、age、showInfo成员),子类Student继承Person。需求分为三种:

  • 希望子类对象能直接调用父类的showInfo方法(类外可访问),子类内部也能直接访问name、age;

  • 不希望子类对象调用父类的showInfo方法(类外不可访问),但允许子类内部访问name、age;

  • 仅允许子类内部访问父类的所有可访问成员,不允许子类的子类(孙子类)、类外访问。

  • 如果只有一种继承方式,显然无法满足这三种差异化的权限需求——三种继承方式的核心作用,就是通过限定父类成员在子类中的访问权限,实现“精准的权限控制”,既保证代码复用,又避免权限泄露,兼顾代码的安全性和可维护性。

    举个通俗的例子:父类的“家产”(成员)分为三类(public、protected、private),子类继承家产时,三种继承方式相当于“不同的继承协议”:

    • public继承:父类的“公开家产”(public)依然公开,“家族内部家产”(protected)依然保留家族内部权限,“私有家产”(private)不可直接动用;

    • protected继承:父类的“公开家产”(public)变为“家族内部家产”,“家族内部家产”(protected)保持不变,“私有家产”(private)不可直接动用;

    • private继承:父类的“公开家产”(public)、“家族内部家产”(protected)均变为子类的“私有家产”,“私有家产”(private)不可直接动用。

    接下来,我们逐一拆解每种继承方式的具体规则、实战用法和适用场景。

    二、核心详解一:public 继承(最常用、推荐优先使用)

    public继承是C++开发中最常用的继承方式,核心特点是“保持父类成员的访问权限不变”——父类的public成员继承后依然是子类的public成员,父类的protected成员继承后依然是子类的protected成员,父类的private成员继承后不可访问(继承但隐藏)。

    核心原则:父类的访问权限在子类中完全保留,子类对象可访问父类继承而来的public成员,子类内部可访问父类继承而来的public、protected成员,类外可通过子类对象访问父类的public成员。

    1. public 继承的访问控制规则(必记)

    父类成员的访问权限子类中对应的访问权限子类内部能否访问子类对象能否访问类外能否通过子类对象访问
    public(公有成员) 子类的public成员
    protected(保护成员) 子类的protected成员 不能 不能
    private(私有成员) 不可访问(继承但隐藏) 不能(需通过父类接口间接访问) 不能 不能

    2. public 继承实战演示(结合场景)

    场景:实现Person父类(public成员showInfo,protected成员name、age,private成员gender),Student子类public继承Person,实现子类独有成员studentId、showStudentInfo,验证访问权限规则。

    #include <iostream>
    #include <string>
    using namespace std;

    // 父类:Person
    class Person {
    public:
    // 父类public成员函数:显示基础信息
    void showInfo() {
    cout << "姓名:" << name << ",年龄:" << age << endl;
    }

    protected:
    // 父类protected成员:姓名、年龄(类内、子类可访问)
    string name;
    int age;

    private:
    // 父类private成员:性别(仅父类内可访问)
    string gender;
    };

    // 子类:Student,public继承Person(核心:保持父类成员权限不变)
    class Student : public Person {
    public:
    // 子类构造函数:调用父类无参构造,初始化自身成员
    Student(string name, int age, string studentId) {
    // 正确:子类内部可访问父类protected成员name、age
    this->name = name;
    this->age = age;
    // this->gender = "男"; // 错误:父类private成员,子类不可直接访问
    this->studentId = studentId;
    }

    // 子类独有public成员函数:显示学生完整信息
    void showStudentInfo() {
    // 正确:子类内部可访问父类public成员showInfo
    showInfo();
    // 正确:子类内部可访问父类protected成员name
    cout << "学号:" << studentId << ",学生姓名:" << name << endl;
    }

    private:
    // 子类独有private成员:学号
    string studentId;
    };

    int main() {
    // 创建子类对象
    Student s("张三", 18, "2024001");

    // 1. 类外通过子类对象访问父类继承而来的public成员showInfo(正确)
    s.showInfo();

    // 2. 类外通过子类对象访问子类独有public成员showStudentInfo(正确)
    s.showStudentInfo();

    // 3. 类外通过子类对象访问父类protected成员name(错误:protected成员类外不可访问)
    // cout << s.name << endl;

    // 4. 类外通过子类对象访问父类private成员gender(错误:不可访问)
    // cout << s.gender << endl;

    return 0;
    }

    运行结果(验证权限规则):

    姓名:张三,年龄:18
    姓名:张三,年龄:18
    学号:2024001,学生姓名:张三

    3. public 继承的适用场景(必记)

    public继承是最符合“is-a”关系的继承方式,适用场景:

  • 希望子类复用父类的公开接口(public成员函数),且允许类外通过子类对象调用这些接口(比如Student复用Person的showInfo方法);

  • 希望子类内部能直接访问父类的保护成员(protected),无需通过父类接口间接访问,简化代码;

  • 希望子类的子类(孙子类)能继承父类的protected成员(public继承后,父类protected成员在子类中依然是protected,孙子类可访问);

  • 大多数“父类-子类”的常规场景(如Person→Student、Person→Teacher、Animal→Dog),均推荐使用public继承。

  • 三、核心详解二:private 继承(最少用、谨慎使用)

    private继承是三种继承方式中使用频率最低的一种,核心特点是“压缩父类成员的访问权限”——父类的public、protected成员继承后,均变为子类的private成员,父类的private成员继承后不可访问(继承但隐藏)。

    核心原则:父类的所有可访问成员(public、protected),均变为子类的私有成员,仅子类内部可访问这些继承而来的成员;子类对象、类外代码,以及子类的子类(孙子类),均无法访问父类继承而来的任何成员。

    1. private 继承的访问控制规则(必记)

    父类成员的访问权限子类中对应的访问权限子类内部能否访问子类对象能否访问子类的子类能否访问
    public(公有成员) 子类的private成员 不能 不能
    protected(保护成员) 子类的private成员 不能 不能
    private(私有成员) 不可访问(继承但隐藏) 不能(需通过父类接口间接访问) 不能 不能

    2. private 继承实战演示(结合场景)

    场景:沿用Person父类,Student子类改为private继承Person,验证“父类可访问成员变为子类private成员”的规则,以及孙子类无法访问父类成员的特点。

    #include <iostream>
    #include <string>
    using namespace std;

    // 父类:Person(与上一节一致)
    class Person {
    public:
    void showInfo() {
    cout << "姓名:" << name << ",年龄:" << age << endl;
    }

    protected:
    string name;
    int age;

    private:
    string gender;
    };

    // 子类:Student,private继承Person(核心:父类public/protected→子类private)
    class Student : private Person {
    public:
    Student(string name, int age, string studentId) {
    // 正确:子类内部可访问父类public/protected成员(已变为子类private)
    this->name = name;
    this->age = age;
    this->studentId = studentId;
    }

    // 子类独有public成员函数:显示学生信息(间接暴露父类成员)
    void showStudentInfo() {
    // 正确:子类内部可访问父类继承而来的showInfo(已变为子类private)
    showInfo();
    cout << "学号:" << studentId << endl;
    }

    private:
    string studentId;
    };

    // 孙子类:GraduateStudent,public继承Student
    class GraduateStudent : public Student {
    public:
    GraduateStudent(string name, int age, string studentId, string major)
    : Student(name, age, studentId) {
    this->major = major;
    }

    void showGraduateInfo() {
    // 错误:父类Student的showInfo是private成员(继承自Person),孙子类不可访问
    // showInfo();
    // 错误:父类Student的name是private成员(继承自Person),孙子类不可访问
    // cout << name << endl;
    cout << "专业:" << major << endl;
    }

    private:
    string major;
    };

    int main() {
    Student s("张三", 18, "2024001");

    // 1. 类外通过子类对象访问父类继承而来的showInfo(错误:已变为子类private成员)
    // s.showInfo();

    // 2. 类外通过子类对象访问子类独有public成员showStudentInfo(正确)
    s.showStudentInfo();

    // 3. 类外通过子类对象访问父类protected成员name(错误:不可访问)
    // cout << s.name << endl;

    GraduateStudent gs("李四", 22, "2024002", "计算机科学");
    gs.showGraduateInfo();

    return 0;
    }

    运行结果(验证权限规则):

    姓名:张三,年龄:18
    学号:2024001
    专业:计算机科学

    3. private 继承的适用场景(必记,谨慎使用)

    private继承的核心是“仅子类内部复用父类成员,不对外暴露任何父类接口”,适用场景极少,仅在以下情况考虑使用:

  • 仅需要子类内部复用父类的成员(变量/函数),但不希望子类对象、类外代码访问这些父类成员;

  • 不希望子类的子类(孙子类)继承父类的任何成员(彻底隔离父类与孙子类的关系);

  • 注意:大多数情况下,private继承的需求可以通过“组合”(子类包含父类对象作为成员)替代,组合比private继承更清晰、更灵活,推荐优先使用组合而非private继承。

  • 示例(组合替代private继承):

    // 组合替代private继承:Student包含Person对象,而非继承Person
    class Student {
    private:
    Person p; // 组合:Student有一个Person对象,复用Person的成员
    string studentId;

    public:
    void showStudentInfo() {
    p.showInfo(); // 间接复用Person的showInfo方法
    cout << "学号:" << studentId << endl;
    }
    };

    四、核心详解三:protected 继承(中等频率,场景有限)

    protected继承的权限介于public和private之间,核心特点是“适度压缩父类成员的访问权限”——父类的public成员继承后变为子类的protected成员,父类的protected成员继承后依然是子类的protected成员,父类的private成员继承后不可访问(继承但隐藏)。

    核心原则:父类的public成员降级为子类的protected成员,protected成员权限不变;子类内部可访问所有继承而来的可访问成员,子类对象、类外代码无法访问;子类的子类(孙子类)可访问这些继承而来的protected成员。

    1. protected 继承的访问控制规则(必记)

    父类成员的访问权限子类中对应的访问权限子类内部能否访问子类对象能否访问子类的子类能否访问
    public(公有成员) 子类的protected成员 不能
    protected(保护成员) 子类的protected成员 不能
    private(私有成员) 不可访问(继承但隐藏) 不能(需通过父类接口间接访问) 不能 不能

    2. protected 继承实战演示(结合场景)

    场景:沿用Person父类,Student子类改为protected继承Person,验证“父类public成员降级为子类protected成员”的规则,以及孙子类可访问这些成员的特点。

    #include <iostream>
    #include <string>
    using namespace std;

    // 父类:Person(与前文一致)
    class Person {
    public:
    void showInfo() {
    cout << "姓名:" << name << ",年龄:" << age << endl;
    }

    protected:
    string name;
    int age;

    private:
    string gender;
    };

    // 子类:Student,protected继承Person(核心:父类public→子类protected)
    class Student : protected Person {
    public:
    Student(string name, int age, string studentId) {
    // 正确:子类内部可访问父类public/protected成员(已变为子类protected)
    this->name = name;
    this->age = age;
    this->studentId = studentId;
    }

    void showStudentInfo() {
    // 正确:子类内部可访问父类继承而来的showInfo(已变为子类protected)
    showInfo();
    cout << "学号:" << studentId << endl;
    }

    private:
    string studentId;
    };

    // 孙子类:GraduateStudent,public继承Student
    class GraduateStudent : public Student {
    public:
    GraduateStudent(string name, int age, string studentId, string major)
    : Student(name, age, studentId) {
    this->major = major;
    }

    void showGraduateInfo() {
    // 正确:父类Student的showInfo是protected成员,孙子类可访问
    showInfo();
    // 正确:父类Student的name是protected成员,孙子类可访问
    cout << "姓名:" << name << ",专业:" << major << endl;
    }

    private:
    string major;
    };

    int main() {
    Student s("张三", 18, "2024001");

    // 1. 类外通过子类对象访问父类继承而来的showInfo(错误:已变为子类protected成员)
    // s.showInfo();

    // 2. 类外通过子类对象访问子类独有public成员showStudentInfo(正确)
    s.showStudentInfo();

    GraduateStudent gs("李四", 22, "2024002", "计算机科学");
    gs.showGraduateInfo();

    return 0;
    }

    运行结果(验证权限规则):

    姓名:张三,年龄:18
    学号:2024001
    姓名:李四,年龄:22
    姓名:李四,专业:计算机科学

    3. protected 继承的适用场景(必记)

    protected继承的核心是“不对外暴露父类接口,但允许子类的子类继承父类成员”,适用场景有限,仅在以下情况考虑使用:

  • 希望子类内部、子类的子类(孙子类)能访问父类的成员,但不希望类外代码、子类对象访问父类的public接口;

  • 希望父类的public接口“仅对家族内部(子类、孙子类)开放”,不对外部开放(比如一些工具类的继承,仅允许子类扩展,不允许外部直接调用父类接口);

  • 注意:protected继承的场景容易与public继承+private成员混淆,需谨慎区分,避免权限设计混乱。

  • 五、三种继承方式核心对比(必记、笔试重点)

    为了方便大家记忆和区分三种继承方式,我们用一个综合表格,对比三者的核心差异(重点关注“父类成员在子类中的权限”和“关键访问能力”),同时补充核心总结,帮你快速掌握重点。

    1. 综合对比表格

    继承方式父类public成员 → 子类权限父类protected成员 → 子类权限父类private成员 → 子类权限子类对象能否访问父类继承成员孙子类能否访问父类继承成员使用频率
    public 继承 public protected 不可访问 能(仅public成员) 能(仅public、protected成员) 极高(推荐优先使用)
    protected 继承 protected protected 不可访问 不能 能(仅public、protected成员) 中等(场景有限)
    private 继承 private private 不可访问 不能 不能 极低(谨慎使用,优先用组合替代)

    2. 核心总结(必记)

  • 继承的本质不变:无论哪种继承方式,子类都会继承父类的所有成员(包括private成员),区别仅在于“继承后成员的访问权限”;

  • 父类private成员的特殊性:无论哪种继承方式,父类的private成员子类都无法直接访问,仅能通过父类提供的public/protected接口间接访问;

  • 权限变化规律:public继承“权限不变”,protected继承“public→protected,protected不变”,private继承“public、protected→private”;

  • 访问能力核心:子类内部均可访问父类的public、protected成员(无论哪种继承方式);子类对象、类外仅能访问public继承中父类的public成员;

  • 开发优先选择:public继承 > 组合 > protected继承 > private继承(大多数场景用public继承,private继承尽量用组合替代)。

  • 六、三种继承方式的高频误区(必避,笔试重点)

    结合初学者的常见错误,聚焦三种继承方式的“权限混淆、继承本质误解”,总结5个高频坑,每个坑对应错误示例和正确写法,帮你少走弯路,规避编译错误和逻辑问题。

    误区1:private继承不继承父类成员(认知错误)

    // 错误认知:Student private继承Person,就不继承Person的showInfo成员
    class Person {
    public:
    void showInfo() { cout << "Person" << endl; }
    };

    class Student : private Person {
    public:
    void test() {
    // 错误:误以为private继承不继承父类成员,不敢调用showInfo
    // showInfo();
    }
    };

    // 正确认知:private继承会继承父类成员,只是父类public/protected成员变为子类private
    class Student : private Person {
    public:
    void test() {
    showInfo(); // 正确:子类内部可访问,已变为子类private成员
    }
    };

    关键提醒:继承方式仅改变“访问权限”,不改变“继承本质”——所有继承方式都会继承父类的所有成员。

    误区2:子类对象能访问父类的protected成员(编译错误)

    class Person {
    protected:
    string name = "张三";
    };

    class Student : public Person {};

    int main() {
    Student s;
    // 错误:无论哪种继承方式,protected成员类外(子类对象)都不可访问
    cout << s.name << endl;
    return 0;
    }

    关键提醒:protected成员的核心权限是“类内、子类可访问”,无论哪种继承方式,类外(包括子类对象)都无法直接访问。

    误区3:protected继承中,父类public成员子类对象能访问(编译错误)

    class Person {
    public:
    void showInfo() { cout << "Person" << endl; }
    };

    class Student : protected Person {};

    int main() {
    Student s;
    // 错误:protected继承中,父类public成员变为子类protected成员,子类对象不可访问
    s.showInfo();
    return 0;
    }

    误区4:滥用private继承,不用组合(设计错误)

    // 错误示例:用private继承复用Person成员,不如用组合清晰
    class Student : private Person {
    public:
    void show() { showInfo(); }
    };

    // 正确示例:用组合替代private继承,代码更清晰、更灵活
    class Student {
    private:
    Person p; // 组合:Student有一个Person对象
    public:
    void show() { p.showInfo(); } // 复用Person的showInfo方法
    };

    关键提醒:private继承的核心需求(子类内部复用父类成员),几乎都可以通过组合实现,组合比private继承更符合“has-a”关系,代码可读性更高。

    误区5:继承方式不影响构造/析构顺序(认知错误)

    错误认知:不同的继承方式,父类与子类的构造/析构顺序不同;

    正确认知:继承方式不影响构造/析构顺序——无论哪种继承方式,都是“先构造父类,再构造子类;先析构子类,再析构父类”(与上一篇博客知识点一致)。

    class Person {
    public:
    Person() { cout << "Person构造" << endl; }
    ~Person() { cout << "Person析构" << endl; }
    };

    // 无论哪种继承方式,构造/析构顺序都一样
    class Student : private Person {
    public:
    Student() { cout << "Student构造" << endl; }
    ~Student() { cout << "Student析构" << endl; }
    };

    int main() {
    Student s;
    return 0;
    }

    运行结果:

    Person构造
    Student构造
    Student析构
    Person析构

    七、综合实战:三种继承方式对比演示(完整可运行)

    结合前文知识点,设计一个完整案例,同时演示三种继承方式的权限差异,对比它们的访问效果,帮助大家巩固所学内容,代码可直接复制运行,直观感受三种继承方式的区别。

    #include <iostream>
    #include <string>
    using namespace std;

    // 父类:Base(基础类,封装三种权限的成员)
    class Base {
    public:
    string public_mem = "父类public成员";
    void public_func() {
    cout << public_mem << endl;
    }

    protected:
    string protected_mem = "父类protected成员";
    void protected_func() {
    cout << protected_mem << endl;
    }

    private:
    string private_mem = "父类private成员";
    void private_func() {
    cout << private_mem << endl;
    }
    };

    // 1. public继承
    class PublicDerived : public Base {
    public:
    void derived_func() {
    // 正确:子类内部可访问父类public/protected成员
    public_func();
    protected_func();
    // private_func(); // 错误:父类private成员不可访问
    cout << "public继承:子类内部访问父类public成员:" << public_mem << endl;
    cout << "public继承:子类内部访问父类protected成员:" << protected_mem << endl;
    }
    };

    // 2. protected继承
    class ProtectedDerived : protected Base {
    public:
    void derived_func() {
    // 正确:子类内部可访问父类public/protected成员(已变为protected)
    public_func();
    protected_func();
    // private_func(); // 错误:父类private成员不可访问
    cout << "protected继承:子类内部访问父类public成员:" << public_mem << endl;
    cout << "protected继承:子类内部访问父类protected成员:" << protected_mem << endl;
    }
    };

    // 3. private继承
    class PrivateDerived : private Base {
    public:
    void derived_func() {
    // 正确:子类内部可访问父类public/protected成员(已变为private)
    public_func();
    protected_func();
    // private_func(); // 错误:父类private成员不可访问
    cout << "private继承:子类内部访问父类public成员:" << public_mem << endl;
    cout << "private继承:子类内部访问父类protected成员:" << protected_mem << endl;
    }
    };

    int main() {
    // 测试public继承
    cout << "==================== public继承测试 ====================" << endl;
    PublicDerived pd;
    pd.derived_func(); // 正确:子类public成员
    pd.public_func(); // 正确:子类public成员(继承自父类)
    // pd.protected_func(); // 错误:子类protected成员,类外不可访问

    // 测试protected继承
    cout << "\\n==================== protected继承测试 ====================" << endl;
    ProtectedDerived pd2;
    pd2.derived_func(); // 正确:子类public成员
    // pd2.public_func(); // 错误:子类protected成员,类外不可访问

    // 测试private继承
    cout << "\\n==================== private继承测试 ====================" << endl;
    PrivateDerived pd3;
    pd3.derived_func(); // 正确:子类public成员
    // pd3.public_func(); // 错误:子类private成员,类外不可访问

    return 0;
    }

    运行结果(清晰对比三种继承方式的权限差异):

    ==================== public继承测试 ====================
    父类public成员
    父类protected成员
    public继承:子类内部访问父类public成员:父类public成员
    public继承:子类内部访问父类protected成员:父类protected成员
    父类public成员

    ==================== protected继承测试 ====================
    父类public成员
    父类protected成员
    protected继承:子类内部访问父类public成员:父类public成员
    protected继承:子类内部访问父类protected成员:父类protected成员

    ==================== private继承测试 ====================
    父类public成员
    父类protected成员
    private继承:子类内部访问父类public成员:父类public成员
    private继承:子类内部访问父类protected成员:父类protected成员

    八、总结:三种继承方式的选择原则(实战必备)

    学习三种继承方式的核心,不仅是掌握它们的访问规则,更重要的是学会在实际开发中“按需选择”——根据权限需求、代码复用场景,选择最合适的继承方式,既保证代码复用,又保证权限安全、结构清晰。

    赞(0)
    未经允许不得转载:171主机测评 » 继承方式:public、private 与 protected 继承
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址