一、Data Member 的绑定(The binding of Data Member)

 extern float x;

 class Point3d
{
public:
Point3d( float, float, float);
float X() const { return x; }
void X( float new_x ) const { x = new_x; }
// ...
private:
float x,y,z;
};

  请问 Point3d::X()传回哪一个x?是class 内部那个还是外部(extern )的那个?大家肯定都会说传回 class 内部的那个,这个回答是真正确的。但是在早期的c++中可不一定哦,早期的C++的取用操作会指向 global x object,这出乎大家的意料。那么早期是如何预防这种错误呢?有以下两种办法:

  (1). 将所有的data member 定义到 class 声明的起始处,以确保正确的绑定,如下:

 extern float x;

 class Point3d
{
float x,y,z;
public:
Point3d( float, float, float);
float X() const { return x; }
void X( float new_x ) const { x = new_x; }
// ...
};

  (2). 把所有的 inline function,不管大小都放在 class 声明之外,即将 member functions 放在class 类里面声明,放到 class 外面进行定义,这里就不举例!

  而今天的代码完全不用像上面那样需要进行考虑,因为现在的编译器对 member functions 的分析会直到整个 class 的声明都出现了才开始。因此在一个 inline member function 函数体内的一个 data member 绑定操作,会在整个 class 声明完成之后发生。但是,对于 member function 的 argument list 并不为真哦!请看下面这个例子:

 #include <iostream>

 using namespace std;

 typedef int length;

 class A
{
private:
typedef float length;
length num;
public:
void setNum( length _num )
{
num = _num;
} }; int main()
{
system("pause");
}

  这个代码并没有运行结果,可以通过编译!但是编译器会给出一个提醒:

    警告 1 warning C4244: “=”: 从“length”转换到“A::length”,可能丢失数据 

  这是为什么呢?因为你的成员变量 num 定义的是 float 型,而你的 setNum() 的参数 _num 是 int 型!咋进行赋值时会进行强制转化,这样编译器会给出如上的警告。由此可说明:对于 member function 的 argument list 并不是取用 class 的 typedef 的 length 而是 全局变量 length,所以在写这样的代码要小心了哦。如果你想要 argment list 的也是用 clas 内的 length ,就把内嵌的 typedef 定义放到他要被用到的地方的前面即可咯!

二、Data Member 的布局(Data Member Layout)

  nonstatic data members 在 class object 中排列的顺序将和其被声明的顺序一致,任何的static data members都不会被放到对象布局之中。而是存放在程序的 数据段(data segment)而与个别的 class 无关。C++的标准是要求在同一个访问级别(也就是 public、private、protected)中,members 的排列只需符合“较晚出现的 members 在 class object 中具有较高地址“,并没要求他们是连续排列的。那么什么东西会被介于被声明的 members 之间呢?

  a. members 的边界调整(alignment) 可能就需要填补一些 bytes. 这样就会隔开两个相连的member啦。

  b. 编译器可能会合成一些内部使用的 data members 以支持整个对象模型。vptr 就是这一类东西,虽然没规定它放在哪个位置。但是一般 vptr 会被放在 class object 的声明的 members 的最后,不过也有编译器会把 vptr 放在 class object 的最前端---不太可能会插到 members 之间啦。

三、Data Member 的存取

  (1). static data members

  我们之前的博文说道不管是没有 class object 、有一个class object 或者 n 个object 这个类的 static data members 都已经存在于程序的 data segment 中了(前提是你得定义了这些 static data members 哈),并且只有一份!大家知道 static data members 可以通过class 的 "::"访问,也可以通过 class object 的"."访问,其实别看他和其他nonstatic 成员一样 通过"."访问,其实它根本不像 nonstatic data members 一样存储在 class object 中啦。这样只是为了方便操作,其实通过"::"访问才是原汁原味的!!!

  既然 static data member 都是一份实体存在 程序的 datasegment 中,那么如果一个程序中的两个类有了同样名字的 static data member 就会导致名称冲突,怎么办?不要怕,编译器会对每个 static data member 进行编码(这种手法叫做:name-mangling),以获得一个独一无二的程序识别代码。当然,每个编译器的 name-mangling 做法都不一样,但无论怎么样他们要做的莫非一下两点:

  a. 一种算法,推导出独一无二的名称。

  b. 万一预编译系统(或编译工具)必须和使用者交谈,那些独一无二的名称就可以轻易的被推到回到原来的名称。(比如报错,提示说你这个 static data member 哪里错误,在给出名字的时候当然是用程序员的命的名称而不是编码后的名称!)

  (2). nonstatic data members

  nonstatic data members 的大小被存放在一个 class object中,并通过 "." 访问。看下面这个例子:

 #include <iostream>

 using namespace std;

 typedef int length;

 class A
{
public:
virtual void foo(){};
int num1,num2,num3;
}; int main()
{
A a;
A *pa = &a ;
printf(" &A::num2 = %p\n",&A::num2);
cout << &a << endl;
cout << &a.num2 << endl;
cout << &(pa->num2) << endl;
return ;
}

  以下是输出结果:

  

  我来解释一下:第一行是data member num2 在class 中的偏移量。第二行是 class A 在内存中的真实地址,第三行和第四行都是num2在内存中的真实地址。

  大家发现图中的:0012FF40( num2 的地址 ) = 0012FF38( a 的首地址) + 00000008( num2 在 class A 中的偏移量); 没?

  所以你不管是通过 class object 的 "." 访问或者通过class pointer 的 "->" 访问nonstatic data member,编译器都是通过:class object 的首地址 + 该 data member 在class 中的 offset (偏移量)。注意:在通过 class pointer ( 类指针)访问 nonstatic data member的时候,如果该 nonstatic data member 是 virtual base classs 的 data member ,那情况会有所不同哦!不过不用担心---虚继承总是不同于其他情况(具体请看 Data 语义学(2) 中对各种情况的介绍),我们多多注意就行了!

Data 语义学(1)的更多相关文章

  1. Data 语义学(2)

    四."继承"与 Data Member (1). 只有继承没有多态 先来看个例子 #include <iostream> using namespace std; cl ...

  2. C++对象模型(五):The Semantics of Data Data语义学

    本文是<Inside the C++ Object Model>第三章的读书笔记.主要讨论C++ data member的内存布局.这里的data member 包含了class有虚函数时 ...

  3. 【C++对象模型】第三章 Data语义学

    1. Data Member 的布局 同一个Access Section(private, public等)中,data member的顺序按照声明顺序排列,但是没有规定需要连续排序.同时编译器可能会 ...

  4. 【深入探索c++对象模型】data语义学二

    单一继承中,base class 和derived class的对象都是从相同的地址开始,其间差异只在于derived class比较大,用以容纳自己的nonstatic members. 若vptr ...

  5. 【深度探索C++对象模型】data语义学

    class X{}; class Y :public virtual X{}; class Z :public virtual X{}; class A :public Y, public Z{}; ...

  6. 深入探索C++对象模型(三)

    Data 语义学 一个class的data members,一般而言,可以表现这个class在程序执行时的某种状态.Nonstatic data members放置的是"个别的class o ...

  7. Linux Debugging(四): 使用GDB来理解C++ 对象的内存布局(多重继承,虚继承)

    前一段时间再次拜读<Inside the C++ Object Model> 深入探索C++对象模型,有了进一步的理解,因此我也写了四篇博文算是读书笔记: Program Transfor ...

  8. CYQ.Data、ASP.NET Aries 百家企业使用名单

    如果您或您所在的公司正在使用此框架,请联系左侧的扣扣,告知我信息,我将为您添加链接: 以下内容为已反馈的用户,(收集始于:2016-08-08),仅展示99家: 序号 企业名称 企业网址 备注 1 山 ...

  9. 终于等到你:CYQ.Data V5系列 (ORM数据层)最新版本开源了

    前言: 不要问我框架为什么从收费授权转到免费开源,人生没有那么多为什么,这些年我开源的东西并不少,虽然这个是最核心的,看淡了就也没什么了. 群里的网友:太平说: 记得一年前你开源另一个项目的时候我就说 ...

随机推荐

  1. 禁止鼠标多次点击选中div中的文字

    <!DOCTYPE html><html><head><meta charset="utf-8"><title>Fire ...

  2. CentOS 6.7增加SWAP交换分区

    任务:新增一个1GB的SWAP分区,并开机自动挂载 1.在/var目录下新增SWAPFILE交换区文件 2.生成SWAP分区 mkswap /var/SWAPFILE 3.激活SWAP分区 swapo ...

  3. 转--Oracle DB 服务器系统时间修改问题与 SCN 关系的深入研究

    论坛里一个朋友说将DB 服务器系统时间往往后修改了3个月(从11年改成10年),启动DB报600的错误. 一. 先做个测试 1.1 关闭DB SQL> shutdown immediate Da ...

  4. 用Java发送邮件

    要用Java发送邮件,除过JDK本身的jar包之外,还需要两个额外的jar包:JavaMail和JAF.当然,如果你使用的JavaEE的JDK,那就不用单独去网上下载了,因为JavaEE的JDK中已经 ...

  5. js--小结⑦---格式转换

  6. Java-分页实例

    1.PageModel.java package com.javaweb; import java.util.List; public class PageModel<E> { priva ...

  7. C#中的三种 加密解密

    刚刚学会的C#的加密与解密(三种)MD5加密/RSA加密与解密/DES加密.也是刚刚申请的blog随便发布一下. (一).MD5加密 MD5 md5 = new MD5CryptoServicePro ...

  8. Xcode的控制台调试命令

    XCode4.0以后,编译器换成了LLVM 编译器 2.0 与以前相比,更加强大:1.LLVM 编译器是下一带开源的编译技术.完全支持C, Objective-C, 和 C++.2.LLVM 速度比 ...

  9. VisualStudio2015内置LocalDB

    简直坑爹,我将之前的VS2013的工程迁移到新电脑的VS2015,然后用的本地数据库居然连接报错了,然后我试连了一下本地数据库, 就是Tools-->Connect to Databases-- ...

  10. SGU 159.Self-Replicating Numbers

    时间限制:0.5s 空间限制:6M 题意:         在b(2<b<36)进制中,找到所有长度为n(0<n<2000)的自守数k,满足k^2%b^n=k,字典序输出.   ...