关于结构体和C++类的内存地址问题

今天终于有时间写点 东西了~ 太爽了  *_*  很多人都知道C++类是由结构体发展得来的,所以他们的成员变量(C语言的结构体只有成员变量)的内存分配机制是一样的。下面我们以类来说明问题,如果 类的问题通了,结构体也也就没问题啦。 类分为成员变量和成员函数,我们先来讨论成员变量。 一个类对象的地址就是类所包含的这一片内存空间的首地址,这个首地址也就对应具体某一个成员变量的地址。(在定义类对象的同时这些成员变量也就被定义了)我们来以一段代码说明问题: //类的定义

class K{
public:
 K(){k = 12;}
 ~K(){}
 int k;
};

//类的使用//... K kTemp;
 printf("%d--%d\n",&kTemp,&kTemp.k);
 printf("%d--%d\n",sizeof(K),sizeof(kTemp.k));
 int *i = (int*)(&kTemp);
 int w = *i;
 printf("%d\n",w); 运行上面的代码,结果如下:1310588--1310588
4--4
12
很明显,类的内存大小和其唯一的成员变量的内存大小是一致的。内存地址也是一致的。他们甚至可以相互转换。换成结构体结果也是一样。网友可以自己运行上面代码来进行确认。 这个时候,可能有人会提出疑问了。那么成员函数又如何?上面得代码就好像类没有任何成员函数一样,根本说明不了问题。 呵呵,所有的函数都是存放在代码区的, 不管是全局函数,还是成员函数。要是成员函数占用类的对象空间,那么将是多么可怕的事情:定义一次类对象就有成员函数占用一段空间。 我们再来补充一下静 态成员函数的存放问题吧:静态成员函数与一般成员函数的唯一区别就是没有this指针,因此不能访问非静态数据成员,就像我前面提到的,所有函数都存放在代码区,静态函数也不例外。所有有人一看到 static 这个单词就主观的认为是存放在全局数据区,那是不对的

c++是一种面向对象的编程语言,它向下保持了对c的兼容,同时也允许程序员能够自由的操控内存,虽然会带来一些问题,但这不是我们要探讨的问题,略过不 表。类是对某种对象的定义,包含变量和方法,也可以理解为现实生活中一类具有共同特征的事务的抽象,他是面向对象语言的基础。所以类是不占有内存的,可是 如果类生成实例那么将会在内存中分配一块内存来存储这个类。

类的实例在内存中是如何分配内存的,有什么需要我们注意的,下面将慢慢到来。

比如下面一个类:

class A

{};

从形式上看,它似乎什么有没有,事实上它不止隐含了一个构造函数和一个析构函数,还有一些操作符重载函数,比如“=”。如果类A被实例话,如A a;在内存会占据多大的空间呢?有人可能会说4,也有人会说0,还有人会说1,说1的就对了,为什么会是1呢?原因有很多,如果我们定义一个数组A b[10];如果上面是0,这样的局面将会很尴尬,所以A这样一个空类,编译器会给它一个字节来填充。

增加一个变量,(字节对齐默认都是4)

class  A

{

public:

int i;

}

类A的实例将占据4个字节的内存,sizeof(A) = 4

变量i 的初值被编译器指定位0xcdcdcdcd。

再增加一个变量,

class A

{

public:

int  i;

int  l;

}

此时按照变量生命的先后顺序,i被放在低地址上,l紧随其后。

实例占用8个字节,sizeof(A) = 4*2 = 8

如果累里面含有函数:

class A

{

public:

int i;

int l;

int add(int x,int y){return (x+y);}

};

有些人可能会说类的大小是12,事实上sizeof(A) = 8;

为什么会这样,这是因为sizeof访问的程序的数据段,而函数地址则被保存在代码段内,所以最后的结果是8.

再看下面这个情况

class A

{

public:

int i;

int l;

static int s;

int add(int x,int y){return (x+y)};

};

此时sizeof(A)大小仍为8,这里留给读者去思考为什么?(^-^)。

当类里面含有虚函数时,情况会如何呢?

class A

{

public:

int i;

int l;

static int s;

virtual void Say(){};

int add(int x,int y){return (x+y)};

};

因为含有虚函数,所以类里面将含有一个虚指针vptr,指向该类的虚表vtbl,一个指针占用四字节的地址,所以sizeof(A) = 12

虚指针放在类实例地址的最低位置,

比如 A *a = new A;

我们可以这样给变量i赋值

int *p = (int *)a;
 p++;
 *p = 1;//把i的值赋为1.

如果类作为派生类,内存将如何分配呢?

这种情况虽然有些复杂,但并不是说不好理解。

他有多少个父类每个父类的大小加起来在加上自身就是sizeof的大小。

//-----C++类对象内存结构[讲得很好] -------

首先介绍一下C++中有继承关系的类对象内存的布局: 
在C++中,如果类中有虚函数,那么它就会有一个虚函数表的指针__vfptr,在类对象最开始的内存数据中。之后是类中的成员变量的内存数据。 
对于子类,最开始的内存数据记录着父类对象的拷贝(包括父类虚函数表指针和成员变量)。 之后是子类自己的成员变量数据。 
对于子类的子类,也是同样的原理。但是无论继承了多少个子类,对象中始终只有一个虚函数表指针。 
 
 
 
为了探讨C++类对象的内存布局,先来写几个类和函数 
首先写一个基类: 
class Base 

public: 
virtual void f() { cout << "Base::f" << endl; } 
virtual void g() { cout << "Base::g" << endl; } 
virtual void h() { cout << "Base::h" << endl; } 
int base; 
protected: 
private: 
}; 
然后,我们多种不同的继承情况来研究子类的内存对象结构。 
1. 无虚函数集继承 
 
//子类1,无虚函数重载 
class Child1 : public Base 

public: 
virtual void f1() { cout << "Child1::f1" << endl; } 
virtual void g1() { cout << "Child1::g1" << endl; } 
virtual void h1() { cout << "Child1::h1" << endl; } 
int child1; 
protected: 
private: 
}; 
这个子类Child1没有继承任何一个基类的虚函数,因此它的虚函数表如下图: 
 
 
我们可以看出,子类的虚函数表中,先存放基类的虚函数,在存放子类自己的虚函数。 
 
2. 有一个虚函数继承 
//子类2,有1个虚函数重载 
class Child2 : public Base 

public: 
virtual void f() { cout << "Child2::f" << endl; } 
virtual void g2() { cout << "Child2::g2" << endl; } 
virtual void h2() { cout << "Child2::h2" << endl; } 
int child2; 
protected: 
private: 
}; 
 
当子类重载了父类的虚函数,则编译器会将子类虚函数表中对应的父类的虚函数替换成子类的函数。 
3. 全部虚函数都继承 
//子类3,全部虚函数重载 
class Child3 : public Base 

public: 
virtual void f() { cout << "Child3::f" << endl; } 
virtual void g() { cout << "Child3::g" << endl; } 
virtual void h() { cout << "Child3::h" << endl; } 
protected: 
int x; 
private: 
}; 
 
 
 
4. 多重继承 
多重继承,即类有多个父类,这种情况下的子类的内存结构和单一继承有所不同。 
 
我们可以看到,当子类继承了多个父类,那么子类的内存结构是这样的: 
子类的内存中,顺序 
 
5. 菱形继承 
 
 
6. 单一虚拟继承 
 
 
虚 拟继承的子类的内存结构,和普通继承完全不同。虚拟继承的子类,有单独的虚函数表, 另外也单独保存一份父类的虚函数表,两部分之间用一个四个字节的0x00000000来作为分界。子类的内存中,首先是自己的虚函数表,然后是子类的数据 成员,然后是0x0,之后就是父类的虚函数表,之后是父类的数据成员。 
如果子类没有自己的虚函数,那么子类就不会有虚函数表,但是子类数据和父类数据之间,还是需要0x0来间隔。 
因此,在虚拟继承中,子类和父类的数据,是完全间隔的,先存放子类自己的虚函数表和数据,中间以0x分界,最后保存父类的虚函数和数据。如果子类重载了父类的虚函数,那么则将子类内存中父类虚函数表的相应函数替换。 
 
7. 菱形虚拟继承 
 
结论: 
(1) 对于基类,如果有虚函数,那么先存放虚函数表指针,然后存放自己的数据成员;如果没有虚函数,那么直接存放数据成员。 
(2) 对于单一继承的类对象,先存放父类的数据拷贝(包括虚函数表指针),然后是本类的数据。 
(3) 虚函数表中,先存放父类的虚函数,再存放子类的虚函数 
(4) 如果重载了父类的某些虚函数,那么新的虚函数将虚函数表中父类的这些虚函数覆盖。 
(5) 对于多重继承,先存放第一个父类的数据拷贝,在存放第二个父类的数据拷贝,一次类推,最后存放自己的数据成员。其中每一个父类拷贝都包含一个虚函数表指 针。如果子类重载了某个父类的某个虚函数,那么该将该父类虚函数表的函数覆盖。另外,子类自己的虚函数,存储于第一个父类的虚函数表后边部分。 
(6) 当对象的虚函数被调用是,编译器去查询对象的虚函数表,找到该函数,然后调用。

来源:http://blog.csdn.net/jimmy54/archive/2010/03/26/5418766.aspx

//-------------------------------------更新中的自己的了解...----------------------------------------------

....20/6/2011....

在C++中,如果类中有虚函数,那么它就会有一个虚函数表的指针__vfptr,在类对象最开始的内存数据中。之后是类中的成员变量的内存数据。

C++类实例以及子类在内存中的分配相关推荐

  1. python 私有和保护成员变量如何实现?—— 单下划线 开始的成员变量叫做保护变量,意思是只有类实例和子类实例能访问到这些变量; 双下划线 开始的是私有成员,意思是只有类对象自己能访问...

    默认情况下,Python中的成员函数和成员变量都是公开的(public),在python中没有类似public,private等关键词来修饰成员函数和成员变量. 在python中定义私有变量只需要在变 ...

  2. Java数组在内存中的分配

    Java数组在内存中的分配 在Java中,数组存储对象的原始值(int,char,...)或引用(也称为指针). 使用"new"创建对象时,会在堆中分配一个内存空间并返回一个引用. ...

  3. jvm性能调优 - 05对象在JVM内存中的分配和流转

    文章目录 前文回顾 大部分正常对象都优先在新生代分配内存 到底什么情况下会触发新生代的垃圾回收? 长期存活的对象会躲过多次垃圾回收? 老年代会垃圾回收吗? 关于新生代和老年代的对象分配,这就完了吗? ...

  4. C++ 数组在内存中的分配

    接前一篇的内容,C++中数组在内存中也有静态分配和动态分配的区别.静态数组建立的方式为:A a[],它在栈上分配空间;动态方式是使用new,malloc在堆上分配. 数组要么在静态存储区被创建(如全局 ...

  5. 【转】PHP对象在内存中的分配

    对像在PHP 里面和整型.浮点型一样,也是一种数据类,都是存储不同类型数据用的, 在运行的时候都要加载到内存中去用,那么对象在内存里面是怎么体现的呢?内存从逻辑上 说大体上是分为4 段,栈空间段.堆空 ...

  6. iOS之深入解析类加载的底层原理:类如何加载到内存中

    一.App 启动与 dylb 加载 App 启动会由 libdyld.dylib 库先于 main 函数调用 start,执行 _dyld_start 方法,然后运用汇编实现调用 dyldbootst ...

  7. python里面的类和对象_Python中类和对象在内存中是如何保存?

    类以及类中的方法在内存中只有一份,而根据类创建的每一个对象都在内存中需要存一份,大致如下图: 如上图所示,根据类创建对象时,对象中除了封装 name 和 age 的值之外,还会保存一个类对象指针,该值 ...

  8. 类中内容在内存中到底是如何分配的呢?

     分类: 一个类,有成员变量:静态与非静态之分:而成员函数有三种:静态的.非静态的.虚的.       那么这些个东西在内存中到底是如何分配的呢?       以一个例子来说明: [html]vi ...

  9. (二十六)、Java数组在内存中如何存放与分配

    Java中有两种类型的数组: 基本数据类型数组: 对象数组: 当一个对象使用关键字"new"创建时,会在堆上分配内存空间,然后返回对象的引用,这对数组来说是一样的,因为数组也是一个 ...

最新文章

  1. 63.死锁和死锁的原因
  2. Arcface v1 论文翻译与解读
  3. 字节跳动AI副总裁离职,将加入清华大学张亚勤团队
  4. python 装饰器有哪些_Python装饰器有哪些常见用途?
  5. python椭圆拟合_opencv python 轮廓特征/凸包/外接矩形/外接圆/拟合矩形/拟合直线/拟合圆...
  6. 探讨:ASP.NET技术的学习顺序问题
  7. JavaWeb:JSON对象和Java对象的相互转换
  8. offset,client,scroll的学习记录
  9. 【Bandit Algorithms学习笔记】EXP3算法理论证明
  10. 百度站内搜索使用教程
  11. android高仿ios11系统,安卓仿苹果iOS11主题APP
  12. IT项目量化管理结构图
  13. Burg法求解AR(p)模型参数(三)Levinson递推公式
  14. 俗语“手握金鱼骨,富贵不用愁”,是啥意思?金鱼骨怎么形成的?
  15. android圆形进度条
  16. 1700802088 韩晓忠
  17. 计算机通讯技术核心期刊有哪些,通信类核心期刊汇总.doc
  18. 单刀双掷的模拟开关MAX4544
  19. 美国大学生足球联赛数据集football——DeepWalk算法
  20. 软路由-Hi-Spider Router-海蜘蛛

热门文章

  1. 抖音gorgon算法04php,抖音xgorgon(0401)获取方法及演示
  2. 网页设计上机考试原题_计算机二级考试即将到达战场,各单位准备!!!!
  3. 敏感词过滤的php代码,php实现敏感词过滤(Trie树)
  4. 安卓期末项目源码_手机随时随地写Python,还可以开发安卓APP,太厉害了!
  5. C++基础复习——C++语言数据类型
  6. 计算机二级vf上机试题,2016年计算机二级《VF》上机题及答案
  7. 10 i lt shell的if_shell脚本----if(数字条件,字符串条件,字符串为空)
  8. 三种常见单片机时钟电路方案,对比其优缺点
  9. 学好单片机必须要了解的的8个电路设计
  10. php中getdistance函数_php代码渗透测试 后门分析篇