类对象和类指针是两个概念

比如有个类:class Test;
定义类对象:Test test;

1.test是类Test的一个实例,

2. test是类Test的一个实例,可以供通过"."运算符访问类中的public成员
定义类指针:Test *p_test; // 此时p_test只是一个指向Test类类型的指针,还没有分配内存。要使用指针,需要p_test = new Test(); 此时p_test指向一个Test类类型的对象,可以通过"->"运算符访问类中的public成员

C++类的存储

c++中最重要的就是类,那么一个类的对象,它在内存中如何存储的?它占 
内存中多少个字节?

首先确定类的构成:

1,数据成员:可以是内置类型,类类型。 
2,函数成员:虚函数,非虚函数

1)数据成员 
内置类型对齐原则 
内置类型就是常用的:char,short,long,int,float,double. 
这些内置类型在类的对象中对齐方式,字节为单位(在c 中结构体也是一样的) 
char 1 
short 2 
long 4 
int 4 
float 4 
fouble 8 
类类型对齐原则(c 中就是结构体对齐原则) 
取类中最长的数据成员作为对齐原则。例如,类中最长为 double,那么就是8 个字节。

2)函数成员 
函数成员是不占用内存中类的对象的字节。为什么呢,你可以这样理解,c++中为了兼容c 
也允许struct 作为类的声明。在c 中struct 是用来声明结构体类型的,只不过c 中的结构 
体没有函数成员。 
同样 c++中允许的函数成员,只不过是类给函数提供了一个作用域。 
一个对象调用函数的时候,可以等价为普通函数的调用 
例如:

class A
{
public:void f(){ cout<<"Hello";}};
A a;
a.f();

a.f()等价于调用f(&a);类中的成员函数(static 成员函数除外)形参中都有个隐含的this 
指针,它指向类对象本身。 
当对象 a 调用f()的时候,它会把a 的地址传给this 指针,所以f()就等价执行

f(const A* this)
{cout<<"Hello";
}

所以对象中并不需要保存函数成员。 
下面举个例子说明类对象的字节数 
eg.1:

class A
{char c;int i;
};
A a;

这对象a 的内存大小sizeof(a)=8(字节为单位) 
解释下: 
c 放在起始位置0,占1 个字节。 
i 是int 要4 字节对齐,所以前面要空3 字节。它要从位置4 开始存储,占4,5,6,7 四 
个位置。 
最后类要按照他最长的数据成员对齐,就是i 也就是4 字节对齐.因为已经占用了8 个字节, 
8 是对齐4 的,所以不用额外增加字节数了。最后sizeof(a)=8。

例子eg.2:

class B
{
doube d;
char c;
A a;//1 中的类类型A
};
B b;

这对象b 的内存大小sizeof(b)=24(字节为单位) 
解释: 
d 放在起始位置0 到7,占8 个字节。 
c 是char 要1 字节对齐,所以放在位置8,占1 个字节。 
b 是类类型,在1 中可以知道它是8 字节对齐的,所以前面要空7 个字节,它从位置16 
开始存储,一直到23,占8 个字节。 
最后类要按照他最长的数据成员对齐,就是d 也就是8 字节对齐,因为已经占用了24 个字 
节,24 是对齐8 的,所以不用额外增加字节数了。最后sizeof(a)=24。 
例子eg.3:

class c
{
char c;
int i1;
double d;
int i2;
};
C c;

你知道sizeof(c)=多少吗? 答案:首先存储字符变量c,0位置存储,占1个字节;然后存储整型变量i1,4个字节对齐,因此从4~7位置存储,占4个字节;然后存储双精度变量d,从8~15位置存储,占8个字节;最后存储整型变量i2,从位置16~19存储占4个字节;最后对齐到最长的(8个字节),及补全20~23位置,整个占24个字节。 
下面说下特殊的,就是 c 中没有的。

【1】类中有虚函数的时候 
我们在一开始的时候,就说了成员函数中有虚函数。c++为了处理多态,所以引入虚函数, 
在一个类对象存储空间中,第一个位置需要4 个字节来存储一个指针。这个指针是指向改 
类的虚函数表的。也就是这个指针的值就是改类的虚函数表的地址。所以就比上面说的多了 
4 个字节。 
例如: 

class D
{
public:
virtual void f(){};
double d;
}
D d; 

sizeof(d)=16;

【2】派生类内存大小 
例如:

class E:D
{
int d0;
char c;
int d1;
};
E e; 

sizeof(e)=32; 
解释: 
基类中有虚函数,所以派生类对象一开始要 4 个字节存储指向虚函数表的指针。 
然后继承 D 中的数据成员double d; 
它要8 字节对齐,所以前面空4 个字节。 
下面就开始存储 d0,c,d1.最后类对齐可计算得到32.

类对象内存结构

首先介绍一下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)有一个虚函数继承 

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) 当对象的虚函数被调用是,编译器去查询对象的虚函数表,找到该函数,然后调用。

类对象和类指针以及类的内存分布相关推荐

  1. C++ 类的行为 | 行为像值的类、行为像指针的类、swap函数处理自赋值

    文章目录 概念 行为像值的类 行为像指针的类 概念 引用计数 动态内存实现计数器 类的swap 概念 swap实现自赋值 概念 行为像值的类和行为像指针的类这两种说法其实蛮拗口的,这也算是 <C ...

  2. python 类 对象 方法 应用_Python 定制类与其对象的创建和应用

    1.创建新类Athlete,创建两个唯一的对象实例sarah james,他们会继承Athlete类的特性 >>> class Athlete: def __init__(self, ...

  3. C++的基类指针指向派生类对象,vector实现多态性

    添加问题: 练手创建一个表示各种不同包裹的继承层次. 在写代码时:要求vector来实现多态性 , 用基类指针变量指向子类对象. 前面的父类和子类定义: //创建一个表示各种不同包裹的继承层次 #in ...

  4. iOS class深入理解: 实例对象、类对象、元类和isa指针

    你要知道的class都在这里 转载请注明出处http://blog.csdn.net/u014205968/ 深入代码理解instance.class object.metaclass 面向对象编程中 ...

  5. C++ const关键字的总结(全局/局部变量、修饰指针和引用、成员函数和数据成员、修饰类对象、const与宏定义的区别、Static与Const的区别)

    const关键字 const关键字 1.什么是const 2.使用原理 2.1.const全局/局部变量 2.2.cosnt修饰指针和引用 2.3.const修饰函数参数 2.4.const修饰函数返 ...

  6. c语言 memset 段错误,段错误之memset对类对象的误用

    1.问题描述 实际项目中,定义一个如下的基类和子类,均包含有虚函数. //基类 class OriginalTask { public: OriginalTask() {}; virtual ~Ori ...

  7. C++类对象在内存中的布局

    目录 一.前言 二.C++ 类对象的内存布局 2.1 只有数据成员的对象 2.2 没有虚函数的对象 2.3 拥有仅一个虚函数的类对象 2.4 拥有多个虚函数的类对象 三.继承关系中的C++类对象内存分 ...

  8. python定义方法继承类_Python类的定义、继承及类对象使用方法简明教程

    Python编程中类的概念可以比作是某种类型集合的描述,如"人类"可以被看作一个类,然后用人类这个类定义出每个具体的人--你.我.他等作为其对象.类还拥有属性和功能,属性即类本身的 ...

  9. C++ 对象和实例的区别,以及用new和不用new创建类对象区别

    起初刚学C++时,很不习惯用new,后来看老外的程序,发现几乎都是使用new,想一想区别也不是太大,但是在大一点的项目设计中,有时候不使用new的确会带来很多问题.当然这都是跟new的用法有关的.ne ...

  10. python定义一个人类_Python类的定义、继承及类对象使用方法简明教程

    Python编程中类的概念可以比作是某种类型集合的描述,如"人类"可以被看作一个类,然后用人类这个类定义出每个具体的人--你.我.他等作为其对象.类还拥有属性和功能,属性即类本身的 ...

最新文章

  1. Android 对BaseAdapter做优化处理
  2. C++编程进阶9(如何将构造函数和非成员函数虚化、无锁单例模式)
  3. Git操作手册|命令速查表
  4. 计算机网络又称为分时多用户,7计算机网络原理期末复习试卷A卷.doc
  5. 在Ubuntu Linux下安装Tomcat
  6. 轻量级锁_一句话撸完重量级锁、自旋锁、轻量级锁、偏向锁、悲观、乐观锁等各种锁 不看后悔系列...
  7. [leetcode]62. 不同路径
  8. c# 指定打开某个路径下的CMD_Node.js 环境在 Windows 系统下安装与搭建教程
  9. 26复杂类型比较,使用Compare .NET objects组件
  10. The Witness - 美不胜收的解谜游戏
  11. 十进制数转化为二进制数的两种方法
  12. 《繁荣的真相》读书笔记
  13. 【土旦】在vue filters中 优雅的使用对象的key、value来替换 if switch多重判断简化流程...
  14. Android中图片的裁剪与压缩
  15. java订餐系统_Java——吃货联盟订餐系统
  16. 比较基因组学分析(Comparative Genomics Analysis)
  17. Unix学习记录(二)
  18. HDU-4540 威威猫系列故事——打地鼠 (动态规划)
  19. Don’t Cross 32 GB!
  20. 菜鸟收藏:Win7开启Aero效果的方法【mfxp】

热门文章

  1. java断点上传分片保存方案_分片上传与断点续传解决方案
  2. widnows监控linux 内存,在windows下监控linux的cpu和内存
  3. ft232r usb uart驱动_构建基于USB的高精度温度传感器电路
  4. 微信小程序时间轴demo_微信小程序 - 时间轴(组件)
  5. asp在线html编辑器,ASP下使用FCKeditor在线编辑器的方法
  6. mysql删除源码_mysql关于删除的命令实例源码删除数据库数据表等
  7. oracle erase,Arc SDE forOracle实现erase空间分析计算
  8. java反序列化为空_Java序列化/反序列化,提供空对象引用
  9. 计算机应用基础253页答案,计算机应用基础作业一(答案)
  10. NHibernate N+1问题实例分析和优化