C/C++编程:对象的差异 叁歲伎倆 2022-10-21 03:44 135阅读 0赞 # 需要多少内存才能表现一个类对象? # * 某**非静态数据成员**的总和大小 * 加上任何由于**对齐**需求而**填补**上去的空间(可能存在**成员**之间,也可能存在**集合体**边界)(**对齐**就是将数值调整到某数的倍数。在32位机器上,通常**对齐**为4bytes(32位),以便bus的运输量达到最高效率) * 加上为了支持virtual而由内部产生的任何额外负担 一个指针/引用,不管它指向哪一种数据类型,指针本身所需的内存大小是固定的。(本质上,一个引用是以一个指针来实现) 举个例子: class ZooAnimal{ public: ZooAnimal(); virtual ~ZooAnimal(); virtual void rotate(); protected: int loc; String name; }; ZooAnimal za("Zoey"); ZooAnimal *pza = &za; 布局如下: ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poaXpoZW5nZ3Vhbg_size_16_color_FFFFFF_t_70] # 指针的类型 # 但是,一个指向ZooAnimal的指针是如何地与一个指向整数的指针或者一个指向template Array的指针有所不同呢? ZooAnimal *px; int *pi; Array<String> *pa; 从内存需求上看,没有什么不同!它们三个都需要由足够的内存来放置一个机器地址,“指向不同类型的各指针”的差异,即不再其指针表示法不同,也不在其内容(代表一个地址)不同,**而是在其所寻址出来的object类型不同**。也就是说,**指针类型会教导编译器如何解释某个特定地址中的内存内容以及大小** 那么,一个指向地址1000而类型为void\*的指针,将涵盖怎样的地址空间呢? 我们不知道!这就是为什么一个类型为`void *`的指针只能含有一个地址,而不能够通过它操作所值的对象的原因。 所以,**转型(cast)是一种编译器指令。大部分情况下它并不改变一个指针所含的真正地址,它只影响“被指出之内存的大小和其内容”的解释方式** # 加上多态之后 # class Bear : public ZooAnimal { oublic: Bear(); ~Bear(); void ratate(); virtual void dance(); protected: enum Dances { ... }; Dances dances_known; int cell_block; }; 继承类的对象与指针的内存布局如下: Bear b("yogi"); Bear *pb = &b; Bear &rb = *pd; ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poaXpoZW5nZ3Vhbg_size_16_color_FFFFFF_t_70 1] 一个Bear指针和一个ZooAnimal指针有什么不同? Bear b; ZooAnimal *pz = &b; Bear *pb = &b; * 它们每个都指向Bear对象的第一个byte。 * 其间的差别是,pd所涵盖的地址包含整个**Bear对象**,pz所涵盖的地址只包含Bear对象中的**ZooAnimal子对象**(ZooAnimal subobject) 除了ZooAnimal子对象中出现的**成员**,你不能使用pz来直接处理Bear的任4任何**成员**,唯一的例外是通过**virtual机制** // 不合法,cell_block不是zooAnimal的一个成员,虽然我们知道pz当前指向一个Bear Object pz->cell_block; // OK: 经过一个明白的向下转型(downcast)操作就没有问题 ((Bear*)pz)->cell_block; // 下面这样更好,但是它是一个运行期操作,成本更高 if(Bear *pb2 = dynamic_cast<Bear *>(pz)){ pb2->cell_block; } //ok: 因为cell_blcck是Bear的一个成员 pd->cell_block; 当我们写: pz->rotate(); 式,pz的类型将在编译时期决定以下两点: * 固定的可用接口。也就是说,pz只能调用ZooAnimal的public接口 * 该接口的访问级别(assess level)。(比如rotate()是ZooAnimal的一个公共成员\[public member\]) 在每一个执行点,pz所指的object类型可以决定rotate()所调用的实体,类型信息的封装并不是维护于pz之中,而是维护于**链接**之中,此**链接**存在于"object的vptr"和"vptr所指的虚函数表(virtual table)"之间。 再看一种情况: Bear b; ZooAnimal za = b; // 这会引起切割 za.rotate(); // 调用ZooAnimal::rotate(); 问: * 为什么rotata()所调用的是ZooAnimal 实体而不是Bear实体? * 另外,如果初始化函数将一个**对象**内容完整拷贝到另一个**对象**中去(对应上面的`=`操作,下称为**指定操作**),为什么za的vptr不指向Bear的**虚函数表**(virtual table) 第二个问题的答案是,编译器在初始化以及指定(assignment)操作之间做出了仲裁。编译器必须确保如果某个对象含有一个或者一个以上的vptrs,那么vptrs的内容不会被**基类对象**初始化或者改变。 第一个问题的答案是,za并不是一个Bear,而是一个ZooAnimal。多态所造成的**一个以上的类型**的潜在力量,并不能实际发挥在**直接存取对象**这件事情上。有一个似是而非的观念:OO程序设计不支持对对象的直接处理,举个例子: ZooAnimal za; Bear b; Panda *pp = new Panda; ZooAnimal *pza; pza = &b; ![在这里插入图片描述][watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poaXpoZW5nZ3Vhbg_size_16_color_FFFFFF_t_70 2] 将pa、b的地址,或者pp所包含的内容(也是个地址)指定给pza,显然不是问题。**一个指针或者引用之所以支持多态,是因为它们并不会引发内存中任何`与类型有关的内存委托操作`,会受到改变的只是它们所指向内粗你的`大小和内存解释方式`而已** 但是,如果试图改变za的大小,就会违反其定义中所签约保护的**资源小需求量**。如果把整个Bead对象指定给za,则会溢出它所配置得到的内存,执行结果当然也就不对了。 **当一个基类对象被直接初始化或者被指定为一个派生类对象时,派生类对象就会被切割,以塞入较小的base type内存中**。派生类型(derived type)将没有留下任何痕迹,多态于是不再呈现,而一个严格的编译器可以在编译时期解析一个**通过该对象而触发的虚函数调用**操作,因而回避virtual机制。如果虚函数被定义为inline,则更有效率上的大收获。 总是,多态时一种威力强大的机制,允许你继承一个抽象的public接口之后,封装相关的类型,需要付出的代价是额外的间接性(在"内存的获得"或者在"类型的决断"上)。C++通过类的指针和引用来支持多态,这种程序设计风格就叫做“面向对象” C++也支持具体的ADT程序风格,如今被称为object-based(OB)。比如String class,一种非多态的数据类型。String class可以展示封装的非多态形式,它提供一个public接口和一个private属性,包括数据和算法,但是不支持类型的扩充。一个OB设计可能比一个对等的OO设计速度更快空间更紧凑。速度快是因为所有的函数引发操作都在编译时期解析完全,对象构建起来不需要vritual机制;空间紧凑是因为每一个类对象不需要负担传统上为了支持vritual机制而需要的额外负担。不管OB设计比较没有弹性 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poaXpoZW5nZ3Vhbg_size_16_color_FFFFFF_t_70]: /images/20221021/ec515a80a91d44b98827c96c569bbceb.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poaXpoZW5nZ3Vhbg_size_16_color_FFFFFF_t_70 1]: /images/20221021/aaa533eb3c1e4e0091e5572a652cf96d.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3poaXpoZW5nZ3Vhbg_size_16_color_FFFFFF_t_70 2]: /images/20221021/67916ec4f5c7433ab28e7ed1b38d8edf.png
相关 Java网络编程:TCP和UDP通信的差异 在Java网络编程中,TCP(Transmission Control Protocol)和UDP(User Datagram Protocol)是两种主要的通信协议。 1. 电玩女神/ 2024年09月10日 21:51/ 0 赞/ 24 阅读
相关 热门AP频段的差异及其在编程中的应用 无线接入点(AP)是用于无线网络连接的设备,它们在不同的频段上工作。不同的热门AP频段在无线通信中有着不同的特点和应用。在本文中,我们将详细探讨一些常见的热门AP频段,以及它们 ╰半夏微凉°/ 2024年02月28日 01:42/ 0 赞/ 11 阅读
相关 JS比较两个对象数组的差异 后台传过来一个对象数组,vue渲染到页面中,然后修改了这个对象数组,可能是增删改了其中的某些对象。但是不是操作一下就发到后台去保存,而是只是前端页面在发生变化,最后点击提交按钮 矫情吗;*/ 2022年12月20日 02:16/ 0 赞/ 145 阅读
相关 C/C++编程:对象的差异 需要多少内存才能表现一个类对象? 某非静态数据成员的总和大小 加上任何由于对齐需求而填补上去的空间(可能存在成员之间,也可能存在集合体边界)(对齐就是将数值调 叁歲伎倆/ 2022年10月21日 03:44/ 0 赞/ 136 阅读
相关 local static对象和non-local static对象在初始化时机上的差异 C++中的static对象是指存储区不属于stack和heap、"寿命"从被构造出来直至程序结束为止的对象。这些对象包括全局对象,定义于namespace作用域的对象,在cl 我就是我/ 2022年09月20日 00:21/ 0 赞/ 92 阅读
相关 lua面向对象编程之点号与冒号的差异详细比较 首先,先来一段在lua创建一个类与对象的代码 <table style="border:1px solid silver; width:877px; border-colla ゝ一世哀愁。/ 2022年08月13日 06:00/ 0 赞/ 269 阅读
相关 cc、gcc、g++、CC的区别概括 gcc是C编译器;g++是C++编译器;linux下cc一般是一个符号连接,指向gcc;gcc和g++都是GUN(组织)的编译器。而CC则一般是makefile里面的一个名字, 妖狐艹你老母/ 2022年08月04日 13:37/ 0 赞/ 336 阅读
相关 男女电影评分差异分析编程 > 计算MovieLens 100k数据集中男性女性用户评分的标准差并输出。 > > 数据集下载http://files.grouplens.org/datasets/mov ゝ一纸荒年。/ 2022年05月10日 14:04/ 0 赞/ 272 阅读
相关 cc、gcc、g++、CC的区别概括 gcc是C编译器;g++是C++编译器;linux下cc一般是一个符号连接,指向gcc;gcc和g++都是GUN(组织)的编译器。而CC则一般是makefile里面的一个名字, 叁歲伎倆/ 2021年12月23日 07:53/ 0 赞/ 442 阅读
还没有评论,来说两句吧...