跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++

C++ 继承中同名成员的隐藏规则与重载辨析

C++ 继承机制的核心在于代码复用与层次结构。重点解析了派生类与基类同名成员的隐藏规则,明确区分了跨作用域的隐藏与同作用域的重载差异。内容涵盖继承访问权限控制、基派生类对象转换的安全性、以及默认成员函数在继承体系中的调用逻辑。通过具体代码示例,演示了如何利用作用域解析符显式访问基类成员,并指出虚析构函数对多态资源管理的关键作用。

Stephaine Walsh发布于 2026/3/29更新于 2026/9/961 浏览
C++ 继承中同名成员的隐藏规则与重载辨析

C++ 继承机制详解:同名成员隐藏与默认函数

继承是面向对象编程(OOP)中实现代码复用的核心手段。它允许我们在保持基类特性的基础上扩展新的方法和属性,从而生成派生类。这种机制体现了由简单到复杂的层次结构,是类设计层面的复用方式。

什么是继承?

假设我们有两个类 Student 和 Teacher,它们都有姓名、地址、电话等公共成员,也有各自独有的成员。如果分别定义,会导致大量重复代码。通过引入一个 Person 基类,将公共部分提取出来,派生类只需关注差异部分,即可避免冗余。

class Person {
public:
    void identity() {
        cout << "identity()" << _name << endl;
    }
protected:
    string _name = "张三";
    int _age = 18;
    string _address;
    string _tel;
};

class Student : public Person {
public:
    void study() {
        cout << "study()" << endl;
    }
protected:
    int _stuid;
};

继承的定义与访问权限

在 C++ 中,基类(父类)和派生类(子类)的关系通过继承方式定义。主要有三种:public、protected、private。

继承后的访问权限变化遵循以下规则:

  1. 基类成员的访问权限在派生类中不会变宽,只会变窄或保持不变。
  2. 使用 class 关键字时,默认继承方式是 private;使用 struct 时,默认是 public。建议显式写出继承方式。
  3. 实际开发中通常使用 public 继承。protected 或 private 继承会导致成员只能在派生类内部使用,降低了扩展性和维护性。

此外,继承不仅限于普通类,还可以继承类模板。例如用继承实现 stack 容器适配器:

template<class T>
class stack : public std::vector<T> {
public:
    void push(const T& x) {
        // 基类是类模板时,需要指定作用域,否则编译报错
        vector<T>::push_back(x);
    }
    void pop() {
        vector<T>::pop_back();
    }
    const T& top() {
        return vector<T>::back();
    }
    bool empty() {
        return vector<T>::empty();
    }
};

基类与派生类的转换

继承关系类似于严格的父子财产关系:

  1. 向上转换:派生类对象可以赋值给基类的指针或引用。这被称为'切片',即只保留派生类中属于基类的部分。
  2. 向下转换:基类对象不能直接赋值给派生类对象。若需转换,必须确保基类指针实际指向的是派生类对象,且最好使用多态类型配合 dynamic_cast 进行安全转换。
class Person {
protected:
    string _name;
    int _age;
};

class Student : public Person {
public:
    int _No;
};

int main() {
    Student sobj;
    Person* pp = &sobj;      // 合法:向上转换
    Person& rp = sobj;       // 合法:向上转换
    Person pobj = sobj;      // 合法:调用拷贝构造
    // sobj = pobj;         // 非法:基类对象不能赋值给派生类对象
    return 0;
}

继承的作用域:隐藏与重载

这是初学者最容易混淆的地方。

重载规则:同一作用域下,函数名相同但参数不同,构成重载。 隐藏规则:派生类和基类有独立的作用域。如果派生类定义了与基类同名的成员,派生类成员将屏蔽基类成员的直接访问。

如果是成员函数的隐藏,只要函数名相同就构成隐藏,无论参数是否相同。

class Person {
protected:
    string _name = "小李子";
    int _num = 111;
};

class Student : public Person {
public:
    void Print() {
        cout << "姓名:" << _name << endl;
        cout << "身份证号:" << Person::_num << endl; // 显式访问基类成员
        cout << "学号:" << _num << endl;             // 访问派生类成员
    }
protected:
    int _num = 999;
};

示例分析:

class A {
public:
    void fun() { cout << "func()" << endl; }
};

class B : public A {
public:
    void fun(int i) { cout << "func(int i)" << i << endl; }
};

int main() {
    B b;
    b.fun(10); // 调用 B::fun(int),正常
    b.fun();     // 编译错误!B 中隐藏了 A::fun(),无法找到无参版本
    return 0;
}

在上面的例子中,A 和 B 的 fun 不构成重载,而是隐藏。因为它们在两个不同的作用域中。调用 b.fun() 时,编译器只在 B 的作用域查找,找不到无参版本,导致编译报错。

派生类的默认成员函数

派生类会自动生成默认成员函数,但有一些特殊规则需要注意:

  1. 构造函数:派生类构造函数必须调用基类构造函数初始化基类部分。如果基类没有默认构造函数,必须在初始化列表中显式调用。
  2. 拷贝构造:派生类拷贝构造需调用基类拷贝构造完成基类部分的复制。
  3. 赋值运算符:派生类 operator= 会隐藏基类的 operator=。如需调用基类版本,需指定作用域 Person::operator=(s)。
  4. 析构函数:析构顺序是先派生类后基类。如果基类析构函数未声明为 virtual,派生类析构函数会与基类析构函数构成隐藏关系,而非重写。这在多态场景下可能导致资源泄漏,因此建议基类析构函数设为 virtual。
class Person {
public:
    Person(const char* name = "peter") : _name(name) {
        cout << "Person()" << endl;
    }
    Person(const Person& p) : _name(p._name) {
        cout << "Person(const Person& p)" << endl;
    }
    Person& operator=(const Person& p) {
        cout << "Person operator=(const Person& p)" << endl;
        if (this != &p) _name = p._name;
        return *this;
    }
    ~Person() {
        cout << "~Person()" << endl;
    }
protected:
    string _name;
};

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

理解这些规则有助于编写更健壮、易维护的 C++ 代码。在实际项目中,尽量避免在继承体系中定义同名成员,以减少混淆。

目录

  1. C++ 继承机制详解:同名成员隐藏与默认函数
  2. 什么是继承?
  3. 继承的定义与访问权限
  4. 基类与派生类的转换
  5. 继承的作用域:隐藏与重载
  6. 派生类的默认成员函数

更多推荐文章

查看全部
  • Rokid 灵珠平台搭建旅游 AR 智能体实战指南
  • 基于 AI 辅助的学生成绩管理系统开发实战
  • 2024 年 AI 绘画现状:为何 MJ 与 SD 讨论热度变化
  • Git 入门指南:从零理解版本控制与团队协作
  • 混合知识库搭建:本地 Docker 部署 Neo4j 图数据库与 Milvus 向量库
  • 钉钉 Webhook 机器人配置与多语言实现指南
  • Ubuntu 22.04 更换镜像源全流程
  • 前端地图 SDK 集成实战:高德/百度/腾讯/Google Maps 接入与封装
  • AI Agent 开发:知识图谱与 Neo4j 实战指南(一)
  • Flutter 三方库 modular_core 在鸿蒙系统上的架构适配与依赖注入实践
  • .NET 集成 GoView 低代码可视化大屏实战指南
  • JavaScript 数组扁平化:不使用 flat 方法的实现方式
  • AI 入门:人工智能发展历程与现状分析
  • PyCharm 安装详细教程及配置指南
  • Python 计算 2025 中秋月相与趣味算法实战
  • Coze 智能体开发入门与实战
  • AI 论文辅助工具功能解析:千笔 AI 核心特性介绍
  • Android Jetpack LiveData 与 Lifecycle 源码深度解析
  • 循环队列(Circular Queue)详解
  • CCF-GESP 2025 年 9 月 C++ 二级真题解析

相关免费在线工具

  • 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

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online