当前位置: 首页 > news >正文

访问继承成员(C++)

访问继承成员

  • 一、访问从基类继承的成员
    • 1.1 继承的基本概念
    • 1.2 如何访问继承的成员
  • 二、为什么要用指针去访问成员
    • 2.1 指针的基本用途
    • 2.2 多态性的实现
    • 2.3 使用指针的其它优势
  • 三、总结
  • 底层了解
    • 背景知识:什么是 vtable 和 vptr?
    • 代码示例:虚函数、多态、vtable
    • 三、底层原理剖析
      • 3.1 编译器的处理方式(伪代码/概念图)
    • 四、对象切片问题
    • 五、vtable 可视化演示(示意图)
    • 六、验证 vtable 的存在(高级)
    • 七、总结:指针 + 虚函数的意义

一、访问从基类继承的成员

1.1 继承的基本概念

当一个类(子类 / 派生类)从另一个类(父类 / 基类)继承时,它可以获取基类的成员变量和成员函数。继承方式分为三种:

class Base {
public:int a;
protected:int b;
private:int c;
};class Derived : public Base {// public继承方式下,a是public,b是protected,c不可访问
};
继承方式基类 public 成员在派生类中基类 protected 成员在派生类中基类 private 成员在派生类中
public仍是 public仍是 protected不可访问
protected变成 protected变成 protected不可访问
private不可访问不可访问不可访问

1.2 如何访问继承的成员

  • 在子类内部,可以直接访问可见的基类成员;

  • 在子类对象中,也可以通过对象或指针访问。

class Base {
public:int x = 5;void show() { std::cout << "Base::show()" << std::endl; }
};class Derived : public Base {
public:void print() {std::cout << "x = " << x << std::endl;  // 直接访问show();  // 调用基类函数}
};int main() {Derived d;d.print();    // 间接访问 Base 成员d.show();     // 直接访问 Base 的函数std::cout << d.x << std::endl;  // 访问 Base 的变量
}

二、为什么要用指针去访问成员

使用指针(特别是基类指针指向派生类对象)是 C++ 多态性的核心。

2.1 指针的基本用途

  • 可以延迟绑定(多态):运行时决定调用哪个函数。

  • 可以动态分配和释放内存。

  • 可以通过统一接口操作不同对象。

2.2 多态性的实现

使用指针的主要好处体现在虚函数的多态调用中:

class Base {
public:virtual void show() { std::cout << "Base" << std::endl; }
};class Derived : public Base {
public:void show() override { std::cout << "Derived" << std::endl; }
};int main() {Base* ptr = new Derived();  // 基类指针指向派生类对象ptr->show();  // 输出 "Derived":多态行为delete ptr;
}

如果不用指针,而是直接用对象,多态将不会生效:

Base obj = Derived();  // 对象切片(object slicing)
obj.show();  // 输出 "Base" 而不是 "Derived"

2.3 使用指针的其它优势

  • 可以轻松地传递对象到函数中(避免对象复制);

  • 能够管理对象生命周期;

  • 在容器中统一管理不同类型的对象指针(如抽象基类指针);

三、总结

场景描述
派生类访问基类成员直接在派生类中使用继承来的成员或方法
为什么用指针访问实现多态(虚函数),避免对象切片,统一接口管理对象
推荐用法:
  • 如果你需要多态,请使用基类指针或引用;

  • 如果只是继承成员、没有虚函数需求,可以直接使用对象访问继承来的成员;

底层了解

背景知识:什么是 vtable 和 vptr?

在 C++ 中,为了实现运行时多态(即虚函数机制),编译器通常采用以下技术:

名称含义
vtable(虚函数表)每个类都有一张表,存储着该类的虚函数地址
vptr(虚函数指针)每个对象中都有一个隐藏的指针,指向其类的 vtable

代码示例:虚函数、多态、vtable

#include <iostream>
using namespace std;class Base {
public:virtual void func1() { cout << "Base::func1()" << endl; }virtual void func2() { cout << "Base::func2()" << endl; }void normal() { cout << "Base::normal()" << endl; }
};class Derived : public Base {
public:void func1() override { cout << "Derived::func1()" << endl; }void func2() override { cout << "Derived::func2()" << endl; }void extra() { cout << "Derived::extra()" << endl; }
};int main() {Base* ptr = new Derived();  // 基类指针指向派生类对象ptr->func1();  // 调用 Derived::func1() —— 多态!ptr->func2();  // 调用 Derived::func2()ptr->normal(); // 调用 Base::normal() —— 非虚函数// ptr->extra(); // 错误:Base 类型中没有 extra()delete ptr;return 0;
}

输出:

Derived::func1()
Derived::func2()
Base::normal()

三、底层原理剖析

3.1 编译器的处理方式(伪代码/概念图)

步骤 1:编译器为每个类建立 vtable

Base_vtable:[0] => &Base::func1[1] => &Base::func2Derived_vtable:[0] => &Derived::func1[1] => &Derived::func2

步骤 2:每个对象内部添加一个隐藏指针 vptr

Base 对象:
+-----------+
| vptr ---> Base_vtable
+-----------+Derived 对象:
+-----------+
| vptr ---> Derived_vtable
+-----------+

步骤 3:调用虚函数时,编译器生成类似这样的代码:

// ptr->func1(); 实际代码类似于:
(*(ptr->vptr)[0])(ptr);  // 从 vtable 取出第一个函数指针调用

四、对象切片问题

Derived d;
Base b = d;  // 对象切片,Derived 部分被丢弃b.func1();   // 调用 Base::func1(),不是 Derived::func1()

因为 vptr 属于对象,当你把一个 Derived 对象赋值给 Base,只拷贝了 Base 的那部分数据(包括其 vptr),所以不会保留 Derived 的虚函数表指针。

五、vtable 可视化演示(示意图)

         +----------------+
Base*| vptr ----------+----> Base_vtable(原指向)|                |+----------------+   [0] → Base::func1[1] → Base::func2如果 ptr = new Derived();+----------------+
Base*| vptr ----------+----> Derived_vtable(多态关键)|                |+----------------+   [0] → Derived::func1[1] → Derived::func2

六、验证 vtable 的存在(高级)

在某些平台下你可以用强制类型转换,**“手动调用虚函数表中的函数”**来验证虚函数表存在(仅供理解用途):

typedef void(*Fun)();int main() {Derived d;Fun* vtable = (Fun*)*(long long*)&d;  // 获取 vptr,再转为函数指针数组vtable[0]();  // 调用 Derived::func1vtable[1]();  // 调用 Derived::func2
}

七、总结:指针 + 虚函数的意义

优点原因
实现运行时多态vtable + vptr
支持接口编程可定义抽象类,派生类实现后用基类指针操作
避免对象切片保留派生类的完整信息
动态绑定灵活程序运行时选择函数行为
http://www.dtcms.com/a/283289.html

相关文章:

  • C语言数据存储与指针
  • 选择亿林数据软件测试服务,为哈尔滨企业数字化转型赋能
  • Rust入门之并发编程基础(三)
  • CSS全面系统教程:从入门到精通网页样式设计
  • Datawhale AI夏令营笔记-TF-IDF方法
  • 深度学习入门-卷积神经网络(CNN)
  • JS修改布局--两列布局,拖拽中间修改左右的宽度
  • GI6E 打破網路封鎖:保護你的通信身份安全
  • AI Agent开发学习系列 - langchain之LCEL(2):LCEL 链式表达解析
  • Java对象的比较
  • 产品更新丨谷云科技 iPaaS 集成平台 V7.6 版本发布
  • C++面向对象创建打印算术表达式树
  • Spring Boot 源码解析之 Logging
  • Vue加密文章密码 VuePress
  • xss-labs靶场(1-5关)
  • 从零开始学习 Redux:React Native 项目中的状态管理
  • 数据结构-1(顺序表)
  • kafka--基础知识点--0
  • 智慧农业新图景:物联网如何精准守护作物生长​
  • 第六届信号处理与计算机科学国际学术会议(SPCS 2025)
  • CrewAI中构建智能体如何选择Crews 和Flows
  • 注意力机制从理论到实践:注意力提示、汇聚与评分函数
  • HertzBeat 监控 SpringBoot 使用案例
  • elf、axf、bin的区别与转换
  • freetds 解决连接SQL SERVER报错Unexpected EOF from the server
  • 基于组学数据的药物敏感性预测模型构建与验证
  • AI时代基础入门
  • 卷积神经网络(CNN)最本质的技术
  • 离线环境中将现有的 WSL 1 升级到 WSL 2
  • list类的常用接口实现及迭代器