`
猫太的鱼
  • 浏览: 233858 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

C++对象中数据成员的内存分布

阅读更多
下面我们再来在 C++类中内存分布情况。
class c1
{
public:
      static int nCount;
      int nValue;
      char c;
      c1();
      virtual ~c1();
      int getValue(void);
      virtual void foo(void);
      static void addCount();
}

我们可以通过 sizeof()得到 c1 对象的大小为 12 个字节。
1、 函数 c1,~c1(),getValue,foo,addCount 为函数,其位于程序的代码段,多个对象共享,
    因此不算在 c1 的 size 中。
2、 static int nCount,因为该变量为静态变量,在 c1 所定义的对象之间共享,其位于程序的
    数据段。其不会随着对象数据的增加而增加。
3、 nValue 和 c 占据内存,其中 nValue 使用了 4 个字节,c 虽然使用了 1 个字节,但由于内
    存对齐的缘故,其也使用了 4 个字节,这样总共占据了 8 个字节。
4、 因为有虚函数,每个类对象要有一个指向虚函数表的指针,每个对象一个,占据 4 个字
    节,虚函数表是位于程序的代码段。
这样 c1 对象的大小为 12 个字节。
总结一下:
1、 静态成员和非静态成员函数,主要占据代码段内存,生成对象,不会再占用内存。
2、 非静态数据成员是影响对象占据内存大小的主要因素,随着对象数据的增加,非静态数
    据成员占据的内存会相应增加。
3、 所有的对象共享一份静态数据成员,所以静态数据成员占据的内存的数量不会随着对象
    的数据的增加而增加。
4、 如果对象中包含虚函数,会增加 4 个字节的空间,不论是有多少个虚函数。
对于 C++中的非内置类型的全局变量,其是属于.data 还是.bss 呢?

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
class c1
{
public:
     c1();
     c1(int i);
     ~c1();
     int n1;
};

c1::c1()
{
    n1=0;
    printf("n1=%d\n",n1);
};
c1::c1(int i)
{
    n1=i;
    printf("n1=%d\n",n1);
};
c1::~c1()
{
    ;
}
c1 g1;
c1 g2=10;
int main()
{
      pause();
      return 0;
}

这是一个 C++的例子,我的问题是 g1 和 g2 分别在哪一个节呢?
按照我们原来的标准,未赋初值的全局变量 g1,将位于.bss 节,赋初值了的 g2,将位于.data
节。可细一想,又不对劲,非内置类型的全局对象,需要调用构造函数将其构造出来,不能
只通过 mmap 将其映射到内存就可以完成的。
头有些大了。
下面我来回答这个问题,实际上 g1 和 g2 全部位于.bss 节,编译器只是为其划分出了一段内
存空间。
我们来验证一下:
>nm –f sysv hello
g1                           |00010a08|  B | OBJECT|00000004| |.bss
g2                           |00010a0c|  B | OBJECT|00000004| |.bss
那什么时候,对对象的成员变量赋值呢?
我们先来运行一下进程。

./hello
n1=0
n1=10
在上面的程序中,main 函数的第一句是 pause(),所以 main 函数刚一进入就停住了,而我们
依然能够看 g1 和 g2 的构造函数打印出来的结果,很显然进入 main 函数之前,运行了 g1
和 g2 的构造函数。
还记得我们前面提到的.init_array 节吗,loader 在将程序焦点转移到 main 函数之前,其会运
行.init_array 函数指针数组中的所有函数。
让我们来查看一下.init_array 中都有那些内容。
>objdump -s hello
.................
Contents of section .init_array:
  108fc 7c840000 14870000
.............................................
108fc 是内存地址的一个序号,我们可以不用管它。7c840000 14870000 才是 init_array 中真
正的内容。
在这里是以小端排序,我们试着翻译一下:
                  应该为
7c840000                        0000847c
                  应该为
14870000                        00008714
我们可以通过查看符号表,看看这两个地址都对应着什么内容。
>nm -n -C hello
0000847c t frame_dummy
000084a4 T c1::c1()
000084e8 T c1::c1()
0000852c T c1::c1(int)
00008574 T c1::c1(int)
000085bc T c1::~c1()
000085e0 T c1::~c1()
00008604 T main
0000861c t __static_initialization_and_destruction_0(int, int)
000086c4 t __tcf_1
000086ec t __tcf_0
00008714 t global constructors keyed to _ZN2c1C2Ev
00008734 T __libc_csu_init
这样就很清楚了,               进程运行时,         在调用 main 之前,  要运行 frame_dummy 和 global constructors
Blog: http://blog.chinaunix.net/u/30686/                                               69
Email:loughsky@sina.com
keyed to _ZN2c1C2Ev。
如果还有兴趣的朋友,可以尝试着对进程进行反编译看看这两个函数到底做了什么事。
我们前文说到,对于 C 程序编写的进程来讲,在运行时,只是通过 mmap 为其数据段分配
了一段虚拟内存,只有在实际用到才会分配物理内存。
而对于 C++编写的程序来讲,那些非内置类型的全局变量,由于在 main 函数之前,需要运
行构造函数,为其成员变量赋值,这时虽然在你的程序里还没有用到,但它已经开始占用了
物理内存。

分享到:
评论

相关推荐

    C++类成员和数据成员初始化总结

    1.分配内存,调用构造函数时,隐式/显示的初始化各数据成员 2.进入构造函数后在构造函数中执行一般计算 1.类里面的任何成员变量在定义时是不能初始化的。 2.一般的数据成员可以在构造函数中初始化。

    C++类与对象

    1.定义一个学生类Student,学生类中有3个私有数据成员:name(姓名)、cls(班级)、...使用构造函数为Student类的对象赋值(name使用动态内存分配空间),display负责显示学生的基本信息,在析构函数中释放动态分配的内存

    深度探索模C++对象模型PDF

    C++对象模型(Th e C++ Object Model) 对象模型如何影响程序(How the Object Model Effects Programs) 1.2 关键词所带来的差异(A Keyword Distinction) 关键词的困扰 策略性正确的struct(The Politically ...

    深度探索C++对象模型 超清版

    C++对象模型(Th e C++ Object Model) 对象模型如何影响程序(How the Object Model Effects Programs) 1.2 关键词所带来的差异(A Keyword Distinction) 关键词的困扰 策略性正确的struct(The Politically ...

    C++实验2 类与对象

    (2)定义一个简单的Computer类,有数据成员芯片(CPU),内存(ram),光驱(cdrom)等等,有两个公有成员函数run、stopo,CPU为CPU类的一个对象,ram为RAM类的一个对象,cdrom为CDROM类的一个对象,定义并实现这个类。...

    谭浩强C语言程序设计,C++程序设计,严蔚敏数据结构,高一凡数据结构算法分析与实现.rar

    3.5.4 字符数据在内存中的存储形式及使用方法 41 3.5.5 字符串常量 41 3.5.6 符号常量 42 3.6 变量赋初值 42 3.7 各类数值型数据之间的混合运算 43 3.8 算术运算符和算术表达式 44 3.8.1 C运算符简介 44 3.8.2 算术...

    面向对象与C++试题.doc

    常数据成员的定义形式与一般常变量的,只不过常数据成员的定义必须出现在类体中 B.常数据成员必须进行初始化,并且不能被更新 C.常数据成员通过构造函数的成员初始化列表进行初始化 D.常数据成员可以在定义时直接...

    详解C++编程中类的声明和对象成员的引用

    而对象是类这种数据类型的一个变量,占用内存空间。 类的声明 类是用户自定义的类型,如果程序中要用到类,必须先进行声明,或者使用已存在的类(别人写好的类、标准库中的类等),C++语法本身并不提供现成的类的...

    浅谈C++对象的内存分布和虚函数表

    c++中一个类中无非有四种成员:静态数据成员和非静态数据成员,静态函数和非静态函数。 1、非静态数据成员被放在每一个对象体内作为对象专有的数据成员。 2、静态数据成员被提取出来放在程序的静态数据区内,为该类...

    《面向对象技术》期末复习资料.doc

    • 非静态数据成员只有在定义了对象之后才存在(C++才会为它分配内存空间);静态数据成员不属于单个对象,即使没有定义它所属类的任何对象时,就已经存在了。 • 非静态数据成员的生存期局限在定义对象的块作用域内...

    《深度探索C++对象模型》(Stanley B·Lippman[美] 著,侯捷 译)

    本书重点:探索“对象导向程序所支持的C++对象模型”下的程序行为。对于“对象导向性质之基础实现技术”以及“各种性质背后的隐含利益交换”提供一个清楚的认识。检验由程序变形所带来的效率冲击。提供丰富的程序...

    新手学习C++入门资料

    C++中new和delete是对内存分配的运算符,取代了C中的malloc和free。 标准C++中的字符串类取代了C标准C函数库头文件中的字符数组处理函数。 C++中用来做控制态输入输出的iostream类库替代了标准C中的stdio函数库。...

    C++课后答案

    如此一来,我们可以使用结构体中的数据成员描述对象的属性,使用结构体 中的函数成员描述对象的操作。 2.2 什么是类的接口,什么是类的实现? 【解答】 一般把仅含函数原型的类声明部分称为类的接口; 一个类的内部...

    C++基础教程完整版

    4. 动态内存分配 Dynamic memory 5. 数据结构 Data Structures 6. 自定义数据类型 User defined data types 5. 面向对象编程 Object-oriented Programming 1. 类,构造函数和析构函数,类的指针 Classes. ...

    现代C++程序设计

    《现代C++程序设计(原书第2版)》图文并茂,通俗易懂,真正做到寓教于乐,是一本难得的C++面向对象设计入门教材。 出版者的话 译者序 前言 第1章 C++概述与软件开发 1.1 什么是C语言和C++ 1.1.1 C和C++历史回顾 1.1.2...

    C++ Primer第四版【中文高清扫描版】.pdf

    【原书名】 C++ Primer (4th Edition) 【原出版社】 Addison Wesley/Pearson 【作者】 (美)Stanley B.Lippman,Josée LaJoie,Barbara E.Moo 【译者】 李师贤 蒋爱军 梅晓勇 林...18.1.1 C++中的内存分配 632 18.1.2 ...

    谭浩强C语言程序设计,C++程序设计,严蔚敏数据结构,高一凡数据结构算法分析与实现.rar )

    3.5.4 字符数据在内存中的存储形式及使用方法 41 3.5.5 字符串常量 41 3.5.6 符号常量 42 3.6 变量赋初值 42 3.7 各类数值型数据之间的混合运算 43 3.8 算术运算符和算术表达式 44 3.8.1 C运算符简介 44 3.8.2 算术...

Global site tag (gtag.js) - Google Analytics