跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
|注册
博客列表

目录

  1. C++继承机制详解:同名隐藏与重载的区别、派生类默认成员函数及栈的实现
  2. 一、继承的定义与访问控制
  3. 二、继承实现栈与类模板的按需实例化
  4. 三、父类与子类对象的赋值兼容转换
  5. 四、继承中的作用域:同名隐藏 vs 函数重载
  6. 五、派生类的默认成员函数
  7. 六、核心问题解答
Python

C++继承机制详解:同名隐藏与重载的区别、派生类默认成员函数及栈的实现

C++继承机制详解:同名隐藏与重载的区别、派生类默认成员函数及栈的实现 面向对象编程的三大特性为封装、继承与多态。重点探讨 C++ 中的继承机制,涵盖访问控制、继承实现栈、作用域规则(同名隐藏与重载的区别)以及派生类的默认成员函数。 一、继承的定义与访问控制 继承允许我们基于现有类创建新类,复用其成员并扩展新功能。例如,Student 和 Teacher 都具备人的共性(姓名、地址、电话),但…

Tesfly发布于 2026/3/30更新于 2026/4/1275K 浏览
C++继承机制详解:同名隐藏与重载的区别、派生类默认成员函数及栈的实现

C++继承机制详解:同名隐藏与重载的区别、派生类默认成员函数及栈的实现

面向对象编程的三大特性为封装、继承与多态。本文重点探讨 C++ 中的继承机制,涵盖访问控制、继承实现栈、作用域规则(同名隐藏与重载的区别)以及派生类的默认成员函数。

一、继承的定义与访问控制

继承允许我们基于现有类创建新类,复用其成员并扩展新功能。例如,Student 和 Teacher 都具备人的共性(姓名、地址、电话),但各有特有属性(学号、职称)。通过继承 Person 类,可以避免代码冗余。

class Person {
public:
    void identity() {
        std::cout << "void identity() " << _name << std::endl;
    }
protected:
    std::string _name = "张三"; // 姓名
    std::string _address;      // 地址
    std::string _tel;          // 电话
private:
    int _age = 18;             // 年龄
};

class Student : public Person {
public:
    void study() { /* ... */ }
protected:
    int _stuid; // 学号
};

class Teacher : public Person {
public:
    void teaching() { /* ... */ }
protected:
    std::string title; // 职称
};

访问控制规则:

  1. 私有成员继承:父类的 成员会被继承到子类对象中,但在子类内部和外部均(无法直接访问)。
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog

更多推荐文章

查看全部
  • Vivado 2019.2安装破解教程:零基础手把手指南
  • VLM经典论文阅读:【综述】An Introduction to Vision-Language Modeling
  • 文心一言 4.0 调用性能优化实战
  • 【verilog语法详解:从入门到精通】
  • Spring AI 实战:从零开发 IDEA 插件版 AI 代码助手
  • DeepSeek 降 AI 指令组合与工具使用指南
  • Visual Studio 2026中Github Copilot的大模型
  • 学得会、做得出、能展示!12493+基于Web的校园二手商品交易系统设计与实现 全套资料打包送,学习更高效!

相关免费在线工具

  • curl 转代码

    解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown 转 HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online

  • HTML 转 Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

private
不可见
  • 保护成员:若成员不希望被类外直接访问,但需在子类中访问,应使用 protected。该限定符主要为继承设计。
  • 访问权限计算:子类中成员的最终访问权限为 Min(父类访问限定符, 继承方式),优先级为 public > protected > private。
  • 继承方式:实际开发中几乎全部使用 public 继承。class 默认继承方式为 private,struct 默认为 public。
  • 二、继承实现栈与类模板的按需实例化

    可以通过继承标准库容器(如 std::vector)快速实现栈结构。但需注意,当基类为类模板时,成员函数中调用基类方法必须显式指定类域,否则编译器在依赖基类中查找时会失败。

    #include <vector>
    #include <iostream>
    
    namespace Keda {
        template<class T>
        class stack : public std::vector<T> {
        public:
            void push(const T& x) {
                // 必须指定类域,否则编译器在模板实例化时无法找到 push_back
                std::vector<T>::push_back(x);
            }
            void pop() { std::vector<T>::pop_back(); }
            const T& top() { return std::vector<T>::back(); }
            bool empty() { return std::vector<T>::empty(); }
        };
    }
    
    int main() {
        Keda::stack<int> st;
        st.push(1);
        st.push(2);
        while (!st.empty()) {
            std::cout << st.top() << " ";
            st.pop();
        }
        return 0;
    }
    

    模板按需实例化:编译器仅实例化实际被调用的成员函数。若未调用 pop(),即使其中未指定类域也不会报错;一旦调用,未指定类域将导致编译错误。

    若需灵活切换底层容器(vector/list/deque),可结合宏定义实现:

    #define CONTAINER std::deque
    // 或 #define CONTAINER std::vector / std::list
    

    三、父类与子类对象的赋值兼容转换

    在 public 继承下,存在以下转换规则(常称为'切片'或'切割'):

    1. 子类对象 -> 父类对象/指针/引用:允许。子类对象中属于父类的部分会被切割出来,拷贝给父类对象,或作为父类指针/引用的目标。
    2. 父类对象 -> 子类对象:不允许。
    3. 父类指针/引用 -> 子类指针/引用:需强制类型转换,且仅当父类指针实际指向子类对象时才安全。

    四、继承中的作用域:同名隐藏 vs 函数重载

    同名隐藏(Name Hiding):当子类与父类存在同名成员时,子类成员会屏蔽父类成员的直接访问。对于成员函数,只要函数名相同即构成隐藏,与参数列表无关。若需访问父类成员,必须显式指定作用域(如 Person::_num 或 Person::func())。

    函数重载(Function Overloading):要求在同一作用域内,函数名相同但参数列表不同。

    class A {
    public:
        void func() { std::cout << "func()" << std::endl; }
    };
    
    class B : public A {
    public:
        void func(int i) { std::cout << "func(int i) " << i << std::endl; }
    };
    
    int main() {
        B b;
        b.func(10);      // 调用 B::func(int),输出 func(int i) 10
        // b.func();     // 编译错误!B::func 隐藏了 A::func,需改为 b.A::func();
        return 0;
    }
    

    建议:尽量避免在继承体系中定义同名成员,以免引发隐藏导致意外行为。

    五、派生类的默认成员函数

    派生类默认生成的成员函数会自动处理父类部分,但需遵循特定规则:

    1. 构造函数:必须调用父类构造函数初始化父类成员。若父类无默认构造函数,必须在子类构造函数的初始化列表中显式调用。
      • 内置类型:默认构造函数不初始化(值不确定)。
      • 自定义类型:调用其默认构造函数。
      • 父类部分:调用父类默认构造函数。
    2. 拷贝构造函数:自动调用父类拷贝构造函数。通过赋值兼容规则,子类对象传给父类引用时会发生切片。
    3. 赋值运算符重载 (operator=):子类的 operator= 会隐藏父类的同名函数。若需正确赋值父类部分,必须显式调用 Base::operator=(other)。
    4. 析构函数:子类析构函数执行完毕后,会自动调用父类析构函数。析构顺序与构造相反:先子类,后父类。无需也不建议显式调用父类析构。

    完整示例:

    class Person {
    public:
        Person(const char* name) : _name(name) { std::cout << "Person()" << std::endl; }
        Person(const Person& p) : _name(p._name) { std::cout << "Person(const Person&)" << std::endl; }
        Person& operator=(const Person& p) {
            std::cout << "Person::operator=" << std::endl;
            if (this != &p) _name = p._name;
            return *this;
        }
        ~Person() { std::cout << "~Person()" << std::endl; }
    protected:
        std::string _name;
    };
    
    class Student : public Person {
    public:
        Student(const char* name, int num) : Person(name), _num(num) {}
        Student(const Student& s) : Person(s), _num(s._num) {}
        Student& operator=(const Student& s) {
            if (this != &s) {
                Person::operator=(s); // 显式调用父类赋值
                _num = s._num;
            }
            return *this;
        }
        ~Student() { /* 自动调用 ~Person() */ }
    protected:
        int _num = 1;
    };
    

    六、核心问题解答

    1. 父类私有成员是否被继承? 是。私有成员会被继承到子类对象内存中,但因访问权限限制,在子类中不可见。
    2. 子类对象一定比父类大吗? 不一定。若子类仅重写方法而未新增数据成员,两者大小可能相同。
    3. 函数重载与同名隐藏的区别? 重载发生在同一作用域,要求同名不同参;隐藏发生在不同作用域(基类与派生类),只要同名即隐藏,与参数无关。
    4. 派生类构造函数是否必须显式调用基类构造函数? 仅当基类没有默认构造函数(或默认构造函数被删除)时才必须显式调用。若基类有默认构造函数,编译器会自动调用。
    5. 构造与析构顺序? 构造:先基类,后派生类。析构:先派生类,后基类。
  • KWDB 运维实战:用 SQL 打通 Metrics 与 CMDB 数据关联
  • 《星辰 RPA 全自动:做一个小红书自动发文机器人》
  • 去除 AI 写作痕迹的提示词技巧与优化流程
  • C++ 异常处理机制详解
  • Google Antigravity AI 编程工具下载与安装指南
  • 小米智能家居Home Assistant接入教程:本地控制与设备兼容问题全解
  • VS Code Copilot 完整使用教程(含图解)
  • 使用星辰 RPA 搭建小红书自动发文机器人
  • 基于 Amazon SageMaker 的 AIGC 应用部署与 Web 集成实践
  • Flask 框架从入门到实战完整指南
  • 使用 Dify 与蓝耘 MaaS 构建企业级智能知识库实战指南
  • 文心一言是百度开发的AI对话工具,支持中文场景下的多轮对话、文本生成、知识问答等