深度探索C++对象模型

2023-05-16

深度探索C++对象模型

  • 什么是C++对象模型:
    • 语言中直接支持面向对象程序设计的部分.
    • 对于各个支持的底层实现机制.
  • 抽象性与实际性之间找出平衡点, 需要知识, 经验以及许多思考.

导读

  • 这本书是C++第一套编译器cfront的设计者所写.
  • 了解C++对象模型, 有助于在语言本身以及面向对象观念两方面层次提升.
    • explicit(明确出现于C++程序代码).
    • implicit(隐藏于程序代码背后).

关于对象

  • 每个非内联(non-inline)成员函数只会诞生一个函数实例. 而内联函数会在每个使用者身上产生一个函数实例.
  • C++在布局以及存取时间上的额外负担主要由虚(virtual)引起的:
    • 虚函数机制(virtual function)用于支持一个有效率的运行期绑定(runtime binding).
    • 虚基类(virtual base class) 用以实现多次出现在继承体系中的基类, 有一个单一而被共享的实例.
    • 额外负担, 派生类转换.
  • 在C++中, 有两种类数据成员: 静态(static) 和非静态(non-static).
    • 三种类成员函数: 静态(static), 非静态(non-static)和虚函数(virtual).
  • 每个数据成员或成员函数都有自己的一个slot(元素, 位置, 槽) --- 针对vtbl(虚表)而言.
  • 成员函数表(member function table)成为了支持虚函数(virtual function)的一个有效方案.

C++对象模型

  • 非静态数据成员被置于每一个类对象中, 静态数据成员被存放在个别的类对象之外.
    • 静态和非静态函数也被存放在个别的类对象之外.
    • 虚函数利用虚表(vbtl)和虚表指针(vptr)设置.
      • 每个类产生一堆指向虚函数的指针, 并放到表格之中.
      • 每个类对象被安插一个指针, 指向相关的virtual table(虚表).
        • vptr的设定与重置都由每一个类的构造, 析构和copy赋值运算符自动完成.
      • 每个类的type_info(类型信息)的对象也由虚表(virtual table)指出, 通常放在表格的第一个槽(slot).
        C++对象模型
  • 在虚拟继承的情况下, 基类不管在继承串链中被派生多少次, 永远只会存在一个实例.
  • class不仅是一个关键字, 它还会引入它所支持的封装和继承的哲学.
  • 某种意义上, 在C++中struct和class这两个关键字是可以互换的.
  • 基类和派生类的数据成员的布局没有谁先谁后的强制规定, 但使用初始化列表时, 必须保持成员变量顺序的一致性.
  • 组合而非继承, 才是把C和C++结合在一起的唯一可行方法.

对象的差异性

  • 三种程序设计范式:
    • 程序模型.
    • 抽象数据类型(基于对象).
    • 面向对象模型.
    • 应该还有一个模板编程(范式模型).
  • 只有通过指针或引用的间接处理基类对象, 才支持面向对象程序设计所需的多态性质.
  • C++中, 多态只存在与public 类体系中, nonpublic的派生行为和void*的指针的多态性, 必须由程序员来显式管理.
    • 隐式转换操作: 把一个派生类的指针转换为一个指向public基类类型的指针.
    • 由虚函数(virtual function)机制: ps->rotate();.
    • 由dynamic_cast和typeid运算符转换: dynamic_cast<base_class *> (derived_class *);.
  • 多态的主要用途是经由一个共同的接口来影响类型的封装, 这个接口一般定义在一个抽象的基类中.
  • 一个指针, 不管它指向那种数据类型, 其本身所需内存大小是固定的, 与计算机的位数一致.
  • 指针类型会教导编译器如何解释某个特定地址中的内存内容及其大小.
  • void*指针能够持有一个地址, 但不能通过 它来操作所指对象, 因为不知道其覆盖怎样的地址空间.
  • 派生类不会新添加虚表指针(vptr, 继续使用基类的指针), 只是覆盖的地址会有所不同.
  • 类型信息的封装并不是维护于指针之中, 而是维护于链接(link)之中, 此链接存在于对象的虚表指针(vptr), 和vptr所指的虚表(virtual table)之间.
  • 编译器必须确保每个对象有一个或一个以上的vptr, 这些vptr的内容不会被基类对象初始化或改变.
  • 一个指针或引用之所以支持多态, 是因为它们并不引发内存中任何与内存相关的内存委托操作, 会改变的只有他们所指内存的"大小和内容解释方式"而已.
  • 将派生类直接用于初始化基类对象时, 派生类对象会被切割以塞入较小的基类类型内存中.
  • C++通过指针(pointer)和引用(reference)来支持多态.

构造函数语义

  • 默认构造函数的构造操作:
    • 会插入一些构造函数的代码.
  • 编译器为未声明任何构造的类, 编译器会为他们合成一个默认的构造函数.
  • 被合成出来的构造函数只满足编译器的需要.
    • 合成的默认构造函数中, 只有基类派生对象成员类对象会被初始化.
    • 所有其他非静态数据成员(如整数, 整数指针, 整数数组等)都不会被初始化.
  • copy 构造函数的构造操作:
    • 默认成员初始化列表, 类似于深拷贝(bitwise copy).
    • 默认构造函数和默认copy构造函数在必要时才由编译器产生出来.
  • 一个类对象可以通过两种方式复制得到, 一种是被初始化(copy constructor), 另一种是被指定(copy assignment operator).
  • 位逐次拷贝(bitwise copy semantics(语义)):
    • 会拷贝每一个位(bit).
  • 什么时候不要位逐次拷贝:
    • 当类内含一个成员对象, 该成员对象中声明了一个copy 构造函数.
    • 类继承的基类中存在一个构造函数.
    • 类声明了一个或多个虚函数.
    • 当类派生自一个继承串连, 其中有一个或多个虚基类时.
  • 当编译器导入一个虚表指针(vptr)到一个类对象中时, 该类就不展现逐次语义(bitwise semantics)了.

程序转换语义(Program Transformation Semantics)

  • 显示的初始化操作(Explicit Initialization):
    • 程序转换有两个阶段:
      • 重写一个定义, 其中的初始化操作会被剥离.
      • 类的copy 构造调用操作会被安插进去.
  • 编译器可能做NRV(Named Return Value)优化操作.
  • 以一个类对象作为另一个类对象的初值的情形, C++允许编译器有大量的自由发挥空间, 以提升程序效率.
  • 必须使用成员初始化列表(member initialization list):
    • 当初始化一个成员引用(reference member)时.
    • 当初始化一个常量成员(const member)时.
    • 当调用一个基类的构造函数, 而该基类拥有一组参数时.
    • 当调用一个成员类的构造函数, 其拥有一组参数时.
  • 编译器会一一操作初始化列表(initialization list), 以适当顺序在构造函数之内安插初始化操作, 在显式之前.
    • 初始化列表中的顺序是由类的成员声明顺序决定的, 不是由初始化列表中的排列顺序决定的.
    • 顺序混乱会造成意想不到的危险.
    • 初始化列表中的项目被放在显示声明代码(explicit user code)之前.

Data语义

  • 一个空类会被编译器安插一个char, 使这个类的两个对象得以在内存中配置独一无二的地址.
  • 空虚基类(Empty virtual base class)已经称为C++面向对象的一个特有术语.
    • 提供了一个虚拟接口, 没有任何数据, 空虚基类被认为是派生对象开头的一部分, 不花费任何派生类的额外空间.
  • 虚基类自读爱香只会在派生类中存在一份实例, 不管它在class继承体系中出现了多少次.
  • 非静态成员数据放置的是个别类对象感兴趣的数据, 静态成员数据放置的是整个类感兴趣的数据.
    • 静态成员变量被放到全局数据段中, 不会影响个别类对象的大小. 不管生成多少个对象, 静态数据成员永远只存在一份实例.
    • 编译器自动加上额外的数据成员, 用以支持某些语言特性.
    • 因为内存对齐(alignment), 边界调整的需要. --- 类对象可能比想象的大.

数据成员的布局

  • 成员变量的排列顺序因编译器而异, 编译器可以随意选一个放在第一个.
  • 在C++中, 在同一access section(private, protected, public等区段)中, 成员的排列只需符合较晚出现的成员变量在类对象中有较高的地址.
  • 静态成员并不需要通过类对象进行访问.
    • 一个静态数据成员的地址是一个指向其数据类型的指针, 并不是一个指向类成员的指针.
  • 对一个非静态数据成员进行存取操作, 编译器需要把类对象的起始地址加上数据成员的偏移位置(offset).
    • 每个非静态数据成员的偏移位置(offset)在编译时期即可知晓.
  • 具体继承(concrete inheritance)并不会增加空间和存取时间上的额外负担.
  • 在每一个类对象(class object)中带入一个vptr, 提供执行期的链接, 使每一个object(对象)能够找到对应的虚表(虚virtual table).
    • 在派生类和基类中, 可能重新设定vptr的值.
      在析构函数中, 可能抹消掉指向类相关虚表(virtual table)的vptr.
  • vptr放在类对象的前端(起始处), 会丧失对C语言的兼容性.
  • 多重继承的问题主要发生于派生类对象和其第二或后继的基类对象之间的转换.
  • 取一个非静态数据成员的地址, 将得到它在类中的偏移量(offset); 取一个绑定于真正类对象身上的数据成员的地址, 将会得到该成员在内存中的真正地址.

函数(Function)语义

  • 静态成员函数:
    • 不能直接存取非静态成员数据.
    • 也不能被声明为const函数.
  • 一般而言, 成员的名称前面会被加上类名称, 以形成独一无二的命名.
  • 静态成员函数没有this指针.
  • 在C++中, 多态(polymorphsim)表示以一个public base class(公有基类)的指针(或引用), 寻址一个派生类对象.
    • 多态机能主要扮演一个传送机制的角色, 可以在程序任何地方采用一组public derived类型.
    • 有了RTTI(runtime tyoe identification)就能够在执行期查询一个多态的指针或多态的引用.
  • 虚拟继承是C++中多重继承中特有的概念, 虚拟继承的一些总结.
  • 深入探索C++对象模型笔记汇总

  • 内联(inline)函数中的局部变量, 再加上有副作用的参数, 可能会导致大量临时性的对象产生.

构造, 析构, 拷贝语义

  • 继承体系中每一个类对象的析构函数都会被调用.
  • 构造函数可能内含大量的隐藏diamante, 因为编译器会扩充每一个constructor, 扩充成都视class 的继承体系而定.
    • 记录在成员初始化列表中的数据成员初始化操作会被放进构造函数本体, 并以成员变量声明顺序为顺序.
    • 如果有一个成员变量没有出现在初始化列表中, 它有一个默认的构造函数, 那么该默认的构造函数必须被调用.
    • 类对象的虚表指针(virtual table pointer)必须被设置初值, 指向适当的虚表(virtual table).
    • 基类的构造函数必须被调用, 以基类的声明顺序为顺序.
    • 虚基类构造函数必须被调用, 从左到右, 从最深到最浅.
  • 如果类没有定义析构函数, 只有在类内的成员对象(基类)拥有析构函数时, 编译器才会自动合成一个出来.

执行期语意

  • C++所有的全局对象都被繁殖在程序的数据段(data segment)中.
  • 运算符new一般由两个步骤完成:
    • 通过适当的new运算符函数实例, 配置所需的内存.
    • 将配置来的对象设立初值.
  • 临时对象的摧毁应该是对完整表达式(full-expression)求值过程中的最后一个步骤.
    • 完整表达式(full-expression)是表达式最外围的那个.
  • 编译器不能消除class类型的局部临时变量, 因为C++back-ends的限制.
    • 可以通过一些优化工具把临时对象放进寄存器.

站在对象模型的尖端

  • 模板template, 异常处理exception handing(EH), 运行时类型识别(runtime type identification, RTTI).
  • 每一个可执行文件中只需要一份模板的实例, 每个编译单位都会拥有一份实例.
  • 只有在成员函数被使用的时候, C++标准才要求他们被实例化.
    • 空间和时间效率的考虑.
    • 尚未实现的机能.
  • 所有与类型相关的检验, 如果牵涉到template参数, 都必须延迟到真正的实例化操作(instantiation)发生, 才得为之.
  • Template中的名称决议法:
    • 定义模板(template)的程序端和实例化模板(template)的程序的区别.
    • 定义模板(template)专注于一般的模板类.
    • 实例化模板(template)专注于特定的实例.
  • 如果一个虚函数被实例化, 其实例化点紧跟在其类的实例化点之后.
  • dynamic_cast运算符可以再执行期决定真正的类型.
  • typeud运算符传回一个const reference, 类型为type_info.
  • 虽然RTTI只适用于多态类(polymorphic classes), 事实上type_info对象也适用于内建类型, 以及非多态的使用者自定义类型.
  • 动态共享函数库, 共享内存.

转载于:https://www.cnblogs.com/longjiang-uestc/p/10777128.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深度探索C++对象模型 的相关文章

  • [iOS] 如何改变一个控制器的大小?

    iOS 如何改变一个控制器的大小 1 改变控制器的VIew大小 这是只能改变控制器里面View的大小 并不会对控制器本身的显示做出视觉改变 2 改变控制的大小 方法 span class hljs comment 重置控制器的大小 span
  • 控件:UIControl 我在UITextFiled输入事件调用到

    下列事件为基类UIControl所支持 xff0c 除非另有说明 xff0c 也适用于所有控件 UIControlEventTouchDown 单点触摸按下事件 xff1a 用户点触屏幕 xff0c 或者又有新手指落下的时候 UIContr
  • iOS抖音点赞动画实现

    前言 hi 大家好 又跟大家见面了 今天给大家分享的是抖音的点赞动画的实现 废话不多说上图 本篇文章主要包含技术点 CAShapeLayer和贝赛尔曲线绘制三角形 组合动画的时间技巧我习惯写完文章的demo都附在文章底部 如果不想看原理的小
  • 必读!!只需10分钟,NAS变身赚钱神器!

    有很多朋友现在家里都有NAS xff0c 今天教大家如何利用NAS的闲置资源薅羊毛 NAS我们正常的用途是存储资料 xff0c 通过网络传输实现多设备文件互通 xff0c 但是在大部分情况下 xff0c 我们的宽带和NAS机能处于闲置状态
  • 如何利用电脑榨干闲置的带宽资源?

    电脑现在是每个家庭的必需品 xff0c 大部分人对电脑的使用方法主要是办公和游戏 xff0c 除了这两个用途外 xff0c 其实大部分人的电脑是处于闲置状态 那么 xff0c 电脑的闲置闲置时间还能做些什么呢 xff1f 今天给大家介绍一个
  • 如何善用家中闲置的带宽资源赚钱(2020版)

    CDN的全称是Content Delivery Network xff0c 即内容分发网络 xff0c 依靠部署在各地的边缘服务器 xff0c 通过中心平台的负载均衡 内容分发 调度等功能模块 xff0c 使用户就近获取所需内容 xff0c
  • 一招将闲置宽带完美利用起来

    随着我们生活水平的提高以及国家对信息化建设的推动 xff0c 大部分家庭的宽带已经进入了高速时代 xff0c 100 200M到处可见 xff0c 甚至于500M也不是什么新鲜事儿了 xff0c 宽带的速率是提高了 xff0c 不过问题也来
  • 十一、 Debian忘记密码重置

    其方式是在GRUB引导菜单下按 e 进入编辑模式直接修改用户密码 重启VPS xff0c 可以在面板重启也可以在VNC上面使用发送 CTRL 43 ALT 43 DEL 按钮直接重启 xff0c 在图示处按 e 键 xff08 若出现BIO
  • 加入共享宽带,让你的闲置宽带循环利用再变现

    共享经济是近些年来发展的一个热点名词 xff0c 因此大家也会看到一些非常多的共享产品出现在市面上 比如说大家熟悉的共享单车 xff0c 共享汽车共享充电宝等等 xff0c 但是不知道大家有没有听说过共享宽带呢 xff1f 宽带几乎是家家户
  • 一招让NAS自给自足

    网络带来了许多便利 xff0c 但又给生活带来了很多烦恼 xff0c 比如微信文档总是过期 xff0c 关键内容经常找不到 xff0c 照片备份太散乱 最近听朋友说听说前任离婚了 xff0c 我突然想重温下与她昨日的温情 xff0c 可是翻
  • 百度网盘撸用户羊毛是怎么一回事

    最近百度网盘事件闹得沸沸扬扬 xff0c 很多吃瓜小伙伴对这次事件的来龙去买不太清楚 xff0c 今天就给大家八一八百度网盘如何反撸用户引发众怒 百度对于该计划的说明 xff1a 用户参加该计划可贡献闲置网络带宽和电脑存储空间给百度 xff
  • 业务流程节点信息提示

    xfeff xfeff 该模块中主要是为了明确用户操作 让用户具体的知道该进行哪一步操作 xff0c 在登陆系统后 xff0c 系统首页中会有下面类似的流程图 xff1a 当用户完成一项操作后 xff0c 要根据流程提示其他用户进行下一步操
  • UbuntuWSL操作PA的BUG记录——AM_HOME环境变量的设定

    2021年5月更 xff0c 发现WSL2是真的香 xff0c 下次还用 x1f604 2021年4月 血亏 xff0c 建议老实用虚拟机做 xff0c WSL还是有很多未完善的地方 xff0c 不适合新手瞎折腾 问题描述 xff1a 当使
  • windows11编译OpenCV4.5.0 with CUDA(附注意事项)

    windows11编译OpenCV4 5 0 with CUDA 从OpenCV4 2 0 版本开始允许使用 Nvidia GPU 来加速推理 本文介绍最近使用windows11系统编译带CUDA的OpenCV4 5 0的过程 文中使用 特
  • OpenCV—矩阵数据类型转换cv::convertTo

    OpenCV 矩阵数据类型转换cv convertTo 函数 void convertTo OutputArray m int rtype double alpha 61 1 double beta 61 0 const 参数 m 目标矩阵
  • Mysql LIMIT使用

    原文出处 xff1a http www jb51 net article 62851 htm Mysql中limit的用法 xff1a 在我们使用查询语句的时候 xff0c 经常要返回前几条或者中间某几行数据 xff0c 这个时候怎么办呢
  • cookies的理解与chrome查看cookie

    Cookies是一种能够让网站服务器把少量数据储存到客户端的硬盘或内存 xff0c 或是从客户端的硬盘读取数据的一种技术 Cookies是当你浏览某网站时 xff0c 由Web服务器置于你硬盘上的一个非常小的文本文件 xff0c 它可以记录
  • 【Qt】Qt多线程开发—实现多线程设计的四种方法

    Qt 使用Qt实现多线程设计的四种方法 文章目录 Qt 使用Qt实现多线程设计的四种方法一 写在前面二 方法一 QThread xff1a 带有可选事件循环的底层API三 方法二 QThreadPool和QRunnable xff1a 重用
  • OpenStack Designate系统架构分析

    前言 OpenStack提供了云计算数据中心所必不可少的用户认证和授权 计算 存储 网络等功能 xff0c 网上已经有不少的文章介绍这些功能的配置 架构分析以及代码详解 但是 针对OpenStack Designate所提供的DNSaaS服
  • 【Qt】Qt线程同步之QWaitCondition

    Qt 线程同步之QWaitCondition 文章目录 Qt 线程同步之QWaitCondition一 简介二 成员函数API xff08 2 1 xff09 等待 wait xff08 2 2 xff09 唤醒一个线程 xff08 2 3

随机推荐

  • Linux下如何用命令连接有线网络

    因为进入tty命令界面 xff0c 想要下东西 xff0c 需要用命令连接有线网络直接输入命令 sudo dhclient eth0
  • Debian下制作deb包

    1 安装相应的编译工具 apt get install dh make dpkg dev debhelper fakeroot build essential Docker中执行dh make出现如下错误 xff1a Cannot get
  • electron在龙芯平台上本地安装使用和打包(二)

    已经打包好的适合在mips平台运行的electron quick start xff0c 点击下载 1 安装electron前的准备 从http www loongnix org index php Electron下载所需软件包 xff0
  • mips版本electron在龙芯平台上的安装

    本文主要讲述如何安装mips版本的electron xff0c 和上两篇不同的是 xff0c 此安装方法只需要用户修改一行代码即可完成 mips架构用户只需要关注本文章的3 2章节 目前用户可以通过此方法安装mips版本的4 1 3 xff
  • 制作PyPI包

    1 环境 本文介绍使用setup py生成pip可以安装的python包以及使用git信息自动生成软件包版本 1 1 python pip版本 python3 version Python 3 7 3 pip 18 1 from usr l
  • python3 对字典去重

    对于一个列表中的多个字典进行去重 1 对key去重 将相同的key合并到一个字典中 2 对元素去重 将一个字典的重复元素去重 代码如下 xff1a span class token comment initializing list spa
  • ffi使用

    编译程序时 xff0c 发现测试用例过不去 xff0c 抽象出其中的测试用例 span class token macro property span class token directive hash span span class t
  • KVM虚拟机内进行GPU计算

    xff08 文章来自作者维护的社区微信公众号 虚拟化云计算 xff09 xff08 目前有两个微信群 kvm虚拟化 和 openstack xff0c 扫描二维码点击 云 交流 xff0c 进群交流提问 xff09 我们知道CUDA是由NV
  • linux下安装及配置jenkins

    jenkins常用的有两种安装方式 xff1a 1 直接下载war包jenkins war xff0c 下载地址https jenkins io download 直接下载 1 1 可以把war包直接部署到servlet容器中 xff0c
  • Ubuntu 中软件的安装、卸载以及查看的方法总结

    说明 xff1a 由于图形化界面方法 xff08 如Add Remove 和Synaptic Package Manageer xff09 比较简单 xff0c 所以这里主要总结在终端通过命令行方式进行的软件包安装 卸载和删除的方法 一 U
  • win10环境下基于anaconda3安装tensorflow的方法以及踩的坑和解决办法

    安装过程 1 首先安装anaconda3 xff0c 下载地址 xff1a https www anaconda com download 2 使用下面的命令创建tensorflow环境 conda create n tensorflow
  • B 站下载教学视频

    B站中的教学视频大都有许多分集 xff0c 而且可能涉及到版权 xff0c 过不了多久可能会失效 xff0c 最好的办法就是先保存下来再说 这里介绍的you get可以下载许多视频网站的视频 xff0c 在此仅介绍B站的下法 虽然速度基本在
  • ssh命令-manpage

    SSH Section User Commands 1 Index Return to Main Contents BSD mandoc NAME ssh OpenSSH SSH 客户端 远程登录程序 总览 SYNOPSIS ssh l l
  • CCF201604-3 路径解析

    题目 xff1a 问题描述 在操作系统中 xff0c 数据通常以文件的形式存储在文件系统中 文件系统一般采用层次化的组织形式 xff0c 由目录 xff08 或者文件夹 xff09 和文件构成 xff0c 形成一棵树的形状 文件有内容 xf
  • powershell批量改文件名递增序号

    目录结构 dir1 file1 jpg dir2 file2 jpg dir3 file3 jpg 把目录下的文件改成目录名 43 001 jpg 需要cd到需要改文件名的目录执行 xff0c 嵌套目录参考get childitem r选项
  • Debian使用apt-get安装软件的问题

    运行环境 xff1a Debian 11 5 正常安装方法 apt get install vim 问题一 xff1a 提示将盘片插入驱动器 34 media cdrom 34 再按回车键 xff0c 如下图 问题二 xff1a 提示软件包
  • 开发必备的100个 Flutter 开源精品项目

    Flutter是谷歌的移动UI框架 xff0c 可以快速在iOS和Android上构建高质量的原生用户界面 Flutter可以与现有的代码一起工作 在全世界 xff0c Flutter正在被越来越多的开发者和组织使用 xff0c 并且Flu
  • 矩阵分解之最小二乘法ALS

    矩阵分解 应用场景是 xff1a 我们要把一个稀疏矩阵分解为两个低秩的矩阵相乘 xff1b 两个低秩的矩阵除了降维之外 xff0c 还分别代表不同的含义 以推荐为例 xff1a 用户点击商品的关系矩阵R则是稀疏的 xff0c 我们分解为两个
  • C/C++使用心得:enum与int的相互转换

    如何正确理解enum类型 xff1f 例如 xff1a enum Color red white blue Color x 我们应说x是Color类型的 xff0c 而不应将x理解成enumeration类型 xff0c 更不应将其理解成i
  • 深度探索C++对象模型

    深度探索C 43 43 对象模型 什么是C 43 43 对象模型 语言中直接支持面向对象程序设计的部分 对于各个支持的底层实现机制 抽象性与实际性之间找出平衡点 需要知识 经验以及许多思考 导读 这本书是C 43 43 第一套编译器cfro