【c++】 继承

继承的概念

所谓继承,就是从"先辈"处获得特性,它是客观世界事物之间的一种重要关系。例如,脊椎动物和无脊椎动物都属于动物,在程序中便可以描述为:脊椎动物和无脊椎动物继承自动物;同时,哺乳动物和两栖动物继承自脊椎动物,而节肢动物和软体动物继承自无脊椎动物。这些动物之间会形成一个继承体系,如下图4-1所示。

在C++中,继承就是在原有类的基础上产生出新类,新类会继承原有类的所有属性和方法。原有的类称为基类或父类,新类称为派生类或子类。派生类同样可以作为基类派生出新类。在多层次继承结构中,派生类上一层的基类称为直接基类,隔层次的基类称为间接基类。例如在图4-1中,脊椎动物是哺乳动物的直接基类,动物是哺乳动物的间接基类。

在C++中,声明一个类继承另一个类的格式如下所示:

复制代码
class 派生类名称:继承方式  基类名称 
{ 
 派生类成员声明 
};

从上述格式可以看出,派生类的定义方法与普通类基本相同,只是在派生类名称后添加冒号":"、继承方式和基类名称。

其中:

  • 派生类(子类):新定义的类,继承自基类。
  • 基类(父类):被继承的类。
  • 继承方式 :控制基类成员在派生类中的访问权限,包括 publicprotectedprivate

继承的基本特点

在类的继承中,有以下几点需要注意。

  1. 构造函数与析构函数不可继承:派生类必须定义自己的构造函数和析构函数,可调用基类的构造函数进行初始化。
  2. 继承全部成员:除构造与析构函数外,派生类继承基类的所有成员(包括私有成员,但访问受限)。
  3. 可扩展性:派生类可新增成员,也可重写(override)基类的成员函数。
  4. 多层次继承:派生类可作为基类继续派生,形成继承链。
  5. 多继承支持:一个派生类可同时继承多个基类(C++支持多继承)。

通过继承,基类中的所有成员(构造函数和析构函数除外)被派生类继承,成为派生类成员。在此基础上,派生类还可以增加新的成员。基类和派生类之间的关系如图4-2所示。

继承的底层原理

  • 内存布局:派生类对象包含基类子对象和自身新增成员。基类部分位于派生类对象内存的起始位置。
  • 成员访问:通过继承,派生类内部可访问基类的公有和保护成员,私有成员虽被继承但不可直接访问(成为"不可访问成员")。
  • 类型关系:公有继承下,派生类对象可视为基类对象,支持赋值兼容(类型兼容)。

继承方式

在继承中,派生类会继承基类除构造函数、析构函数之外的全部成员。从基类继承的成员,其访问属性除了成员自身的访问属性,还受继承方式的影响。类的继承方式主要有三种:public(公有继承)、protected(保护继承)和private(私有继承)。不同的继承方式会影响基类成员在派生类中的访问权限。

public(公有继承)

特点 :基类的 publicprotected 成员在派生类中保持原有访问权限;private 成员在派生类中不可访问。
语义 :派生类"是一个"基类(is-a关系),强调接口继承。
适用场景:大多数继承场景,尤其是需要实现多态和类型兼容的情况。

基类成员访问权限 在派生类中的访问权限
public public
protected protected
private 不可访问
CPP 复制代码
#include <iostream>
using namespace std;

class Base {
public:
    int publicVar;
protected:
    int protectedVar;
private:
    int privateVar;
};

class PublicDerived : public Base {
public:
    void accessTest() {
        publicVar = 10;        // √ 可访问,仍然是public
        protectedVar = 20;     // √ 可访问,仍然是protected
        // privateVar = 30;    // × 编译错误:不可访问
    }
};

int main() {
    PublicDerived obj;
    obj.publicVar = 100;       // √ 外部可访问
    // obj.protectedVar = 200; // × 外部不可访问
    // obj.privateVar = 300;   // × 外部不可访问
    
    cout << "公有继承示例:" << endl;
    cout << "publicVar: " << obj.publicVar << endl;
    return 0;
}

protected(保护继承)

特点 :基类的 publicprotected 成员在派生类中变为 protectedprivate 成员不可访问。
语义 :派生类"以受保护方式继承"基类实现,强调派生类及其子类可访问基类功能,但对外隐藏。
适用场景:设计中间类,既想复用基类实现,又不希望对外暴露基类接口。

基类成员访问权限 在派生类中的访问权限
public protected
protected protected
private 不可访问
CPP 复制代码
#include <iostream>
using namespace std;

class Base {
public:
    int publicVar;
protected:
    int protectedVar;
private:
    int privateVar;
};

class ProtectedDerived : protected Base {
public:
    void accessTest() {
        publicVar = 10;        // √ 可访问,变为protected
        protectedVar = 20;     // √ 可访问,仍然是protected
        // privateVar = 30;    // × 编译错误:不可访问
    }
    
    void show() {
        cout << "publicVar: " << publicVar << endl;
    }
};

int main() {
    ProtectedDerived obj;
    // obj.publicVar = 100;    // × 外部不可访问,已变为protected
    // obj.protectedVar = 200; // × 外部不可访问
    // obj.privateVar = 300;   // × 外部不可访问
    
    obj.show();  // √ 通过公有成员函数间接访问
    return 0;
}

private(私有继承)

特点 :基类的 publicprotected 成员在派生类中变为 privateprivate 成员不可访问。
语义 :派生类"以私有方式继承"基类实现(has-a关系),仅当前类可使用基类功能,后续派生类无法继续访问。
适用场景:实现组合(composition)的另一种方式,强调实现复用而非接口继承。

基类成员访问权限 在派生类中的访问权限
public private
protected private
private 不可访问
注意
  • 不可访问成员:指在类内和类外均无法直接访问的成员,通常由私有继承或私有成员在派生类中形成。
  • 保护继承 vs 私有继承 :在直接派生类中二者效果类似,但在进一步派生时,保护继承的成员仍为 protected,私有继承的成员变为不可访问。
CPP 复制代码
#include <iostream>
using namespace std;

class Base {
public:
    int publicVar;
protected:
    int protectedVar;
private:
    int privateVar;
};


class PrivateDerived : private Base {
public:
    void accessTest() {
        publicVar = 10;        // √ 可访问,变为private
        protectedVar = 20;     // √ 可访问,变为private
        // privateVar = 30;    // × 编译错误:不可访问
    }
    
    void show() {
        cout << "publicVar: " << publicVar << endl;
    }
};

// 进一步派生类
class FurtherDerived : public PrivateDerived {
public:
    void furtherTest() {
        // publicVar = 100;    // × 不可访问(在PrivateDerived中是private)
        // protectedVar = 200; // × 不可访问(在PrivateDerived中是private)
    }
};

int main() {
    PrivateDerived obj;
    // obj.publicVar = 100;    // × 外部不可访问,已变为private
    // obj.protectedVar = 200; // × 外部不可访问
    // obj.privateVar = 300;   // × 外部不可访问
    
    obj.show();  // √ 通过公有成员函数间接访问
    return 0;
}

类型兼容

类型兼容(也称赋值兼容)指在公有继承体系中,派生类对象可替代基类对象使用。这是C++多态性的基础之一。

兼容规则

Derived 公有继承自 Base,则:
派生类对象可赋值给基类对象(发生对象切片)

cpp 复制代码
Derived d;
Base b = d;  // 仅复制Base部分

派生类对象可初始化基类引用

cpp 复制代码
Derived d;
Base& br = d;

派生类指针可赋值给基类指针

cpp 复制代码
Derived* pd = new Derived();
Base* pb = pd;

派生类对象可作为基类类型参数传递

cpp 复制代码
void func(Base b);
Derived d;
func(d);  // 传递派生类对象

底层原理与注意事项

  • 对象切片(Object Slicing):当派生类对象赋值给基类对象时,仅基类部分被复制,派生类新增成员被"切割",可能导致数据丢失。
  • 动态绑定:通过基类指针或引用调用虚函数时,实际执行的是派生类重写的版本(多态)。
  • 访问限制:通过基类指针/引用只能访问基类中定义的成员,无法直接访问派生类新增成员。

典型应用场景

  • 函数参数泛化:使用基类类型作为参数,可接受任意派生类对象。
  • 容器存储:使用基类指针容器(如 vector<Base*>) 统一管理不同派生类对象。
  • 多态调用:通过虚函数机制实现运行时行为动态确定。
相关推荐
寻寻觅觅☆6 小时前
东华OJ-基础题-106-大整数相加(C++)
开发语言·c++·算法
fpcc6 小时前
并行编程实战——CUDA编程的Parallel Task类型
c++·cuda
l1t7 小时前
在wsl的python 3.14.3容器中使用databend包
开发语言·数据库·python·databend
赶路人儿7 小时前
Jsoniter(java版本)使用介绍
java·开发语言
ceclar1237 小时前
C++使用format
开发语言·c++·算法
码说AI8 小时前
python快速绘制走势图对比曲线
开发语言·python
Gofarlic_OMS8 小时前
科学计算领域MATLAB许可证管理工具对比推荐
运维·开发语言·算法·matlab·自动化
lanhuazui108 小时前
C++ 中什么时候用::(作用域解析运算符)
c++
charlee448 小时前
从零实现一个生产级 RAG 语义搜索系统:C++ + ONNX + FAISS 实战
c++·faiss·onnx·rag·语义搜索
星空下的月光影子8 小时前
易语言开发从入门到精通:补充篇·网络爬虫与自动化采集分析系统深度实战·HTTP/HTTPS请求·HTML/JSON解析·反爬策略·电商价格监控·新闻资讯采集
开发语言