龙空技术网

C++对象的底层原理都在这了,还敢说你学不会?(2)

一只码农 737

前言:

今天看官们对“子类中调用父类成员的关键字是什么”都比较关怀,小伙伴们都想要知道一些“子类中调用父类成员的关键字是什么”的相关内容。那么小编也在网络上收集了一些有关“子类中调用父类成员的关键字是什么””的相关内容,希望看官们能喜欢,我们快快来了解一下吧!

Part3 3、Data语意学

class X{};class Y : public virtual X {};class Z : public virtual X {};class A : public Y,public Z {};sizeof(X) //1sizeof(Y) //4sizeof(Z) //4sizeof(A) //8

X为1是因为编译器的处理,在其中插入了1个char,为了让其对象能在内存中有自己独立的地址Y,Z是因为虚基类表的指针A 中含有Y和Z所以是8

每一个类对象大小的影响因素:非静态成员变量的大小virtual特性内存对齐

3.1 数据成员的绑定

如果类的内部有typedef,请把它放在类的起始处,因为防止先看到的是全局的和这个typedef相同的冲突,编译器会选择全局的,因为先看到全局的

3.2 数据成员的布局

非静态成员变量的在内存中的顺序和其声明顺序是一致的但是不一定是连续的,因为中间可能有内存对齐的填补物 virtual机制的指针所放的位置和编译器有关

3.3 成员变量的存取

静态变量都被放在一个全局区,与类的大小无关,正如对其取地址得到的是与类无关的数据类型,如果两个类有相同的静态成员变量,编译器会暗自为其名称编码,使两个名称都不同非静态成员变量则是直接放在对象内,经由对象的地址和在类中的偏移地址取得,但是在继承体系下,情况就会不一样,因为编译器无法确定此时的指针指的具体是父类对象还是子类对象

3.4 继承下的数据成员

在下面给定的两个类中依次讨论不同情况:

原本的数据模型

在单一继承没有虚函数的情况下布局图

单一继承且无虚函数

这种情况下常见错误:可能会重复设计一些操作相同的函数,我们可以把某些函数写成inline,这样就可以在子类中调用父类的某些函数来实现简化把数据放在同一个类中和继承起来的内存布局可能不同,因为每个类需要内存对齐

分层继承的布局

可见内存大了100%

容易出现的不易发现的问题:

继承下易犯错误

当加上多态之后,对空间上增加的额外负担包括:

导入一个虚函数表,表中的个数是声明的虚函数的个数加上一个或两个slots(用来支持运行类型识别)在每个对象中加入vptr,提供执行期的链接,使每一个类能找到相应的虚函数表加强构造函数,使它能够为vptr设定初值,让它指向对应的虚函数表,这可能意味着在派生类和每一个基类的构造函数中,重新设定vptr的值加强析构函数,使它能够消抹“指向类的相关虚函数表”的vptr,vptr很可能以及在子类析构函数中被设定为子类的虚表地址。析构函数的调用顺序是反向的,从子类到父类

以下是三种情况:不同的继承下会有不同的布局

函数

多重继承

**单一继承特点:**派生类和父类对象都是从相同的地址开始,区别只是派生类比较大能容纳自己的非静态成员变量

多重继承下会比较复杂

多重继承关系

一个派生对象,把它的地址指定给最左边的基类,和单一继承一样,因为起始地址是一样的,但是后面的需要更改,因为需要加上前面基类的大小,才能得到后面基类的地址

多重继承数据分布

虚继承

STL标准库中使用的虚继承:

虚继承例子

虚继承关系:

虚继承数据在内存中的分布

3.5 对象成员的效率

程序员如果关心程序效率,应该实际测试,不要光凭推论、常识判断或假设。优化操作并不一定总是能够有效运行,我不止一次以优化方式来 编译一个已通过编译的正常程序,却以失败收场

3.6 指向数据成员的指针

vptr通常放在起始处或尾端,与编译器有关,C++标准允许放在类中的任何位置 取某个类成员变量的地址,通常取到得的是在类的首地址的偏移位置例如 & Point3d::z; 将得到在类的偏移位置,最低限度是类的成员大小总和,而这个偏移量通常都被加上了1如果用一个真正绑定类对象(也就是使用 . 操作符访问成员变量)去取地址,得到的将会是内存中真正的地址

在多重继承下,若要将第二个积累的指针和一个与派生类绑定的成员结合起来,那么将会因为需要加入偏移量而变得相当复杂

标签: #子类中调用父类成员的关键字是什么