C++/C#中堆栈、对象内存模型、深浅拷贝、Array.Clone方法
转载自:http://blog.csdn.net/jarvischu/article/details/6425534
目录
1. C++/C#中对象内存模型..................................................................................................... 1
1.1. 栈内存与堆内存...................................................................................................... 1
1.1.1. 栈内存........................................................................................................... 1
1.1.2. 堆内存........................................................................................................... 1
1.1.3. 栈内存与堆内存比较................................................................................... 1
1.2. 值类型与引用类型...................................................................................................2
1.3. 内存模型.................................................................................................................. 3
2. 深拷贝和浅拷贝..................................................................................................................4
2.1. 案例1.........................................................................................................................4
2.2. 理解.......................................................................................................................... 5
2.3. 案例2........................................................................................................................ 6
3. System.Array的Clone() ........................................................................................................8
1. C++/C#中对象内存模型
1.1. 栈内存与堆内存栈内存与堆内存
1.1.1. 栈内存
l 由编译器自动分配和释放
l 用于保存一些局部变量、函数的参数等
理解:
l int a;
那么编译器会自动在栈中给变量a分配一个sizeof(int)大小的内存
1.1.2. 堆内存
l 由程序员手动申请和[释放]
在C++中,通过new申请,编译器不会释放,必须通过delete释放。
在C#中,通过new申请,因编译器的GC(垃圾回收机制),程序员省去了delete操作
l char* p = new char[10];//C++
char[] p = new char[10];//C#
首先,编译器分配一个栈内存保存变量p
之后,编译器分配一个堆内存,大小为sizeof(char)*10
最后,将堆内存的首地址存放在p中。
1.1.3. 栈内存与堆内存比较
u 内存分配
栈:后进先出;由编译器分配相应类型大小;分配的大小受限于栈区大小
堆:随意分配;由程序员手动申请指定大小;分配的大小受限于计算机的虚拟内存
u 效率
栈:高
堆:相对栈低
1.2. 值类型与引用类型
u 简单理解
值类型变量保存的是实际数据,引用类型变量保存的实际数据所在的内存地址。
u 存储区别
值类型数据存储在栈内存中,引用类型变量对应的实际数据存储在堆内存(变量本身存储在栈内存中,通常是四个字节,保存着一个地址数值)
u C++类型
值类型:int,float,double,char,enum
引用类型:array,structure,class
u C#类型
值类型:struct,enum
引用类型:class,delegate,array,interface
为什么这里没有int,float等类型了?(写漏了?)
回答这个问题,请看MSDN中的一句话:
Types that you define by using the struct keyword are value types; all the built-in numeric types are structs. (C#)
这里有三点强调一下:
ü struct(结构体)类型是值类型
ü built-in numberic types(内建数值类型,即int,float等)都是结构体,如int对应了System.Int32结构体
ü 具MSDN记载,System.Int32就是int的别名
图 1 C#中类型概览
1.3. 内存模型
图 2 值类型内存模型
图 3 引用类型内存模型(C#为例,C++类似)
2. 深拷贝和浅拷贝
2.1. 案例1
以图3中的Person类为例。(Name 和 Age是Person类的public 属性)
上面的语句执行结果是什么?
这里涉及到深拷贝和浅拷贝,理解了这两个概念后,问题的答案也就出来了。
2.2. 理解
当将对象a赋值给另一个对象b时,就会执行拷贝函数。
深拷贝:将a 的完完全全复制一份,然后赋值给b(Copy Value)(可以理解成副本)
浅拷贝:将a 的引用复制一份,然后赋值给b(Copy Reference)(可以理解成别名)
图4中:
Person b = al;
浅拷贝,它只是将对象a的引用(就是变量a,更确切的说是a中保存的内存地址)赋值给了变量b。这样,a和b就指向了同一块内存。
Person c = new Person(a.Name,a.Age);
深拷贝,它将对象a的引用以及它所指向的内存区的数据都复制了一份,然后赋值给了b。这样,a和b都指向了各自地址不同,但数据相同的内存区。
这样,上面问题的答案也出来了,输出:
//2013-10-23修正
Chu
Jarvis
图 4 深浅拷贝
2.3. 案例2
好了,我们趁热打铁,再看另一种情况。
Country类和Person类的定义如下:
则下面的代码输出结果是什么?
- Country china = new Country(“中国”,960);
- Person a = new Person(“Jarvis”,22,china);
- Person b = a;
- Console.WriteLine(a.MotherLand.CountryName);
- Console.WriteLine(b.MotherLand.CountryName);
- china.CountryName = “共和国”;
- Console.WriteLine(a.MotherLand.CountryName);
- Console.WriteLine(b.MotherLand.CountryName);
这个案例中涉及了两次浅拷贝,
一次是MotherLand = land; ,
一次是Person b = a;。
它体现的内容是:
将一个含有对象类型属性(MotherLand)的对象(a),直接赋值给另一个对象(b)。
那么这个拷贝(很明显是浅拷贝)的过程究竟是怎么样的?内存中数据的状态又是如何的?
答案是:
共和国
共和国
(你答对了么?)
看一下我的图解。
图 5 含对象属性的拷贝
如果将2处的“中国”变成了“共和国”,当然输出结果是两行“共和国”咯
3. System.Array的Clone()方法
为什么要特意讲这个Clone()方法呢?它有什么特别么?
说特别,可能有点特别,但是讲它的主要原因不在于此,而是因为这个方法让我纠结了很久,因为MSDN中,说它是浅拷贝。可是下面的代码输出的结果却与我想的不一样:
- int[] arrOriginal = { 1, 2, 3, 4 };
- int[] arrCopyDirect = arrOriginal; //直接浅拷贝
- int[] arrCopyClone = (int[])arrOriginal.Clone(); //Clone方法
- ShowArray(arrOriginal); //ShowArray方法就是简单的把数组的每个元素都显示出来ShowArray(arrCopyDirect);
- ShowArray(arrCopyClone);
- arrOriginal[0] = 0;
- Console.WriteLine();
- ShowArray(arrOriginal);
- ShowArray(arrCopyDirect);
- ShowArray(arrCopyClone);
输出的结果是:
说明Clone方法是把arrOriginal数组中的元素都另外拷贝了一份赋值给了arrCopyClone数组,所以赋值结束后,改变arrOriginal数组的元素的值(arrOriginal[0] = 0),arrCopyClone就不受影响了(arrCopyClone[0] = 1),这是浅拷贝么?
有点让人疑惑。
但是如果用一个Person类类型的数组而不是int类型的数组去测试时,可以发现的确源数组A赋值给新数组B 后,A中元素如果改变,B也相应的变化了。
这个让我在费了很长时间才理解,这个过程中,我就对上面提到的几个知识点做了进一步理解。
最终我找到了答案,MSDN上的解释是:
A shallow copy of an Array copies only the elements of the Array, whether they are reference types or value types, but it does not copy the objects that the references refer to. The references in the new Array point to the same objects that the references in the original Array point to.
意思就是说:
不管数组中的元素是值类型(Value Types,如int)还是引用类型(Reference Types,如Person),该方法都是将数组的所有元素都拷贝到另一个数组中。
这样的结果就是:如果原数组中的元素都是值类型,这个就相当于深拷贝,如果原数组中的元素是引用类型,那么就相当于浅拷贝。
所以我认为,MSDN上直接把它说成是Shallow Copy还是有待商榷的。
C++/C#中堆栈、对象内存模型、深浅拷贝、Array.Clone方法的更多相关文章
- C++对象内存模型1(堆栈模型)
对象内存模型 一. 栈(Stack) VS. 堆(heap) 栈 由系统自动管理,以执行函数为单位 空间大小编译时确定(参数+局部变量) 函数执行时,系统自动分配一个stack 函数执行结束时,系统立 ...
- C++对象内存模型1(堆栈模型)(转)
对象内存模型 一. 栈(Stack) VS. 堆(heap) 栈 由系统自动管理,以执行函数为单位 空间大小编译时确定(参数+局部变量) 函数执行时,系统自动分配一个stack 函数执行结束时,系统立 ...
- 理论与实践中的 C# 内存模型
转载自:https://msdn.microsoft.com/magazine/jj863136 这是该系列(包含两部分内容)的第一部分,这部分将以较长的篇幅介绍 C# 内存模型. 第一部分说明 C# ...
- C#的对象内存模型
转载自:http://www.cnblogs.com/alana/archive/2012/07/05/2577893.html C#的对象内存模型: 一.栈内存和堆内存1.栈内存 由编译器自动分配和 ...
- C++对象内存模型2 (虚函数,虚指针,虚函数表)
从例子入手,考察如下带有虚函数的类的对象内存模型: class A { public: virtual void vfunc1(); virtual void vfunc2(); void func1 ...
- (转)c#对象内存模型
对象内存模型 C#的对象内存模型写这篇博客的主要目的是为了加深自己的理解,如有不对的地方,请各位见谅. C#的对象内存模型: 一.栈内存和堆内存1.栈内存 由编译器自动分配和释放,主要用来保存一些局部 ...
- 从零开始学C++之虚继承和虚函数对C++对象内存模型造成的影响
首先重新回顾一下关于类/对象大小的计算原则: 类大小计算遵循结构体对齐原则 第一个数据成员放在offset为0的位置 其它成员对齐至min(sizeof(member),#pragma pack(n) ...
- Swift 对象内存模型探究(一)
本文来自于腾讯Bugly公众号(weixinBugly),未经作者同意,请勿转载,原文地址:https://mp.weixin.qq.com/s/zIkB9KnAt1YPWGOOwyqY3Q 作者:王 ...
- 对C++对象内存模型造成的影响(类/对象的大小)
首先重新回顾一下关于类/对象大小的计算原则: 类大小计算遵循结构体对齐原则 第一个数据成员放在offset为0的位置 其它成员对齐至min(sizeof(member),#pragma pack(n) ...
随机推荐
- zookeeper系列之四—coordinate
http://zookeeper.apache.org/doc/trunk/zookeeperProgrammers.html
- 第三个 android控件
android控件以及控件对应的属性:
- ios照片获取,拍照功能
// // HYBPhotoPickerManager.h // ehui // // Created by 黄仪标 on 14/11/26. // Copyright (c) 2014年 黄 ...
- UVA 10817 十一 Headmaster's Headache
Headmaster's Headache Time Limit:3000MS Memory Limit:0KB 64bit IO Format:%lld & %llu Sub ...
- Linux安装配置JDK
如果想看Windows下的安装,请访问此链接: http://www.cnblogs.com/yoyotl/p/5101321.html 1. 去官网下载Linux版本的jdk安装包,(我下载的是ta ...
- [HDOJ1231]最大连续子序列
混了好几个地方的博客,还是觉得博客园比较靠谱,于是决定在这里安家落户了.本人本科生一个,希望各位巨巨多多指教~ Hello World! 单独一个象征性的问候实在是太low了,还是决定来点实质性的.. ...
- CUBRID学习笔记 3 net连接数据库并使用cubrid教程示例
接上文 数据库安装好后,也可以测试语句了. 下面我们用c#写一个控制台程序,连接数据库,并读取数据. 一 下载驱动 net版的下 CUBRID ADO.NET Data Provider 9.3.0 ...
- sqlserver前面加N解释
From : http://lzde360.blog.163.com/blog/static/6780720820111026112033917/ 加上 N 代表存入数据库时以 Unicode 格式存 ...
- JAVA 调用命令并输出
public class test10 { /** * @param args */ public static void main(String[] args) throws Exception { ...
- vi_命令
1.文件末尾新增一行: 非编辑模式下,按大写的G 跳到最后一行. 然后按小写的O键,增加一行. 2.删掉一行: 非编辑状态下,光标定位到要删除的那一行,然后 dd 3.删字符 在非插入模式下,把光标 ...