.NET的堆和栈03,引用类型对象拷贝以及内存分配

在" .NET的堆和栈01,基本概念、值类型内存分配"中,了解了"堆"和"栈"的基本概念,以及值类型的内存分配。我们知道:当执行一个方法的时候,值类型实例会在"栈"上分配内存,而引用类型实例会在"堆"上分配内存,当方法执行完毕,"栈"上的实例由操作系统自动释放,"堆"上的实例由.NET Framework的GC进行回收。

在" .NET的堆和栈02,值类型和引用类型参数传递以及内存分配"中,我们了解了值类型参数和引用类型参数在传递时的内存分配情况。

而本篇的重点要放在:引用类型对象拷贝以及内存分配。

主要包括:
■  引用类型对象拷贝 成员都是值类型
■  引用类型对象拷贝 包含引用类型成员

引用类型对象拷贝 成员都是值类型

public struct Shoe{
               public string Color;
           }

           public class Dude
           {
                public string Name;
                public Shoe RightShoe;
                public Shoe LeftShoe;

                public Dude CopyDude()
                {
                     Dude newPerson = new Dude();
                     newPerson.Name = Name;
                     newPerson.LeftShoe = LeftShoe;
                     newPerson.RightShoe = RightShoe;

                     return newPerson;
                }

                public override string ToString()
                {
                     return (Name + " : Dude!, I have a " + RightShoe.Color  +
                         " shoe on my right foot, and a " +
                          LeftShoe.Color + " on my left foot.");
                }

           }

           public static void Main()
           {
                  Dude Bill = new Dude();
                  Bill.Name = "Bill";
                  Bill.LeftShoe = new Shoe();
                  Bill.RightShoe = new Shoe();
                  Bill.LeftShoe.Color = Bill.RightShoe.Color = "Blue";

                  Dude Ted =  Bill.CopyDude();
                  Ted.Name = "Ted";
                  Ted.LeftShoe.Color = Ted.RightShoe.Color = "Red";

                  Console.WriteLine(Bill.ToString());
                  Console.WriteLine(Ted.ToString());
           }

输出结果:
Bill : Dude!, I have a Red shoe on my right foot, and a Red on my left foot
Ted : Dude!, I have a Red shoe on my right foot, and a Red on my left foot

以上,当引用类型的属性、成员都是值类型的时候,拷贝是完全拷贝。

引用类型对象拷贝 包含引用类型成员

把Shoe由struct值类型改成引用类型class。

public class Shoe{
               public string Color;
           }

再次运行,输出结果:
Bill : Dude!, I have a Red shoe on my right foot, and a Red on my left foot
Ted : Dude!, I have a Red shoe on my right foot, and a Red on my left foot

当Dude类包含引用类型属性Shoe的时候,在托管堆上的情况是这样的:

拷贝后,2个Dude的Shoe类型的属性指向了同一个托管堆内的Shoe实例,改变Shoe的值会同时影响到2个Dude。

很显然,这不是我们期望的完全拷贝,如果做到完全拷贝呢?
--实现ICloneable接口

ICloneable接口的Clone()方法,允许我们在拷贝的时候,进行一些自定义设置。

让引用类Shoe实现ICloneable接口。

public class Shoe : ICloneable
             {
                  public string Color;

                  public object Clone()
                  {
                      Shoe newShoe = new Shoe();
                      newShoe.Color = Color.Clone() as string;
                      return newShoe;
                  }
             }

以上,Shoe的string类型属性Color之所以可以使用Color.Clone()方法,是因为string也实现了ICloneable接口;又由于Clone()返回类型是object,所以,在使用Color.Clone()方法之后,需要把object转换成string类型。

现在,在Dude类的CopyDude()方法中,当拷贝Shoe类型属性的时候,就可以使用Shoe独有的拷贝方法Clone()。

public Dude CopyDude()
                {
                    Dude newPerson = new Dude();
                     newPerson.Name = Name;
                     newPerson.LeftShoe = LeftShoe.Clone() as Shoe;
                     newPerson.RightShoe = RightShoe.Clone() as Shoe;

                     return newPerson;
                }

客户端程序。

public static void Main()
           {
                  Dude Bill = new Dude();
                  Bill.Name = "Bill";
                  Bill.LeftShoe = new Shoe();
                  Bill.RightShoe = new Shoe();
                  Bill.LeftShoe.Color = Bill.RightShoe.Color = "Blue";

                  Dude Ted =  Bill.CopyDude();
                  Ted.Name = "Ted";
                  Ted.LeftShoe.Color = Ted.RightShoe.Color = "Red";

                  Console.WriteLine(Bill.ToString());
                  Console.WriteLine(Ted.ToString());            

           }

输出结果:  
Bill : Dude!, I have a Blue shoe on my right foot, and a Blue on my left foot
Ted : Dude!, I have a Red shoe on my right foot, and a Red on my left foot

这正是我们期望的完全拷贝!

完全拷贝,托管堆上的情况是这样的:

当然也可以让同时包含值类型和引用类型成员,同时需要拷贝的类实现ICloneable接口。

public class Dude: ICloneable
           {
                public string Name;
                public Shoe RightShoe;
                public Shoe LeftShoe;

                public override string ToString()
                {
                     return (Name + " : Dude!, I have a " + RightShoe.Color  +
                         " shoe on my right foot, and a " +
                          LeftShoe.Color + " on my left foot.");
                    }
                  #region ICloneable Members

                  public object Clone()
                  {
                       Dude newPerson = new Dude();
                       newPerson.Name = Name.Clone() as string;
                       newPerson.LeftShoe = LeftShoe.Clone() as Shoe;
                       newPerson.RightShoe = RightShoe.Clone() as Shoe;

                       return newPerson;
                  }

                  #endregion
             }

客户端调用。

public static void Main()
           {
               Class1 pgm = new Class1();

                  Dude Bill = new Dude();
                  Bill.Name = "Bill";
                  Bill.LeftShoe = new Shoe();
                  Bill.RightShoe = new Shoe();
                  Bill.LeftShoe.Color = Bill.RightShoe.Color = "Blue";

                  Dude Ted =  Bill.Clone() as Dude;
                  Ted.Name = "Ted";
                  Ted.LeftShoe.Color = Ted.RightShoe.Color = "Red";

                  Console.WriteLine(Bill.ToString());
                  Console.WriteLine(Ted.ToString());            

           }

输出结果: 
Bill : Dude!, I have a Blue shoe on my right foot, and a Blue on my left foot.
Ted : Dude!, I have a Red shoe on my right foot, and a Red on my left foot.

也是我们期望的完全拷贝!

参考资料:
C# Heap(ing) Vs Stack(ing) in .NET: Part III

".NET的堆和栈"系列包括:

.NET的堆和栈01,基本概念、值类型内存分配

.NET的堆和栈02,值类型和引用类型参数传递以及内存分配

.NET的堆和栈03,引用类型对象拷贝以及内存分配

.NET的堆和栈03,引用类型对象拷贝以及内存分配,布布扣,bubuko.com

时间: 2024-08-05 17:44:20

.NET的堆和栈03,引用类型对象拷贝以及内存分配的相关文章

.NET 基础 一步步 一幕幕[面向对象之堆、栈、引用类型、值类型]

堆.栈.引用类型.值类型 内存分为堆和栈(PS:还有一种是静态存储区域 [内存分为这三种]),值类型的数据存储在栈中,引用类型的数据存储在堆中. 堆.栈: 堆和栈的区别: 栈是编译期间就分配好的内存空间,因此你的代码中必须就栈的大小有明确的定义:局部值类型变量.值类型参数等都在栈内存中. 堆是程序运行期间动态分配的内存空间,你可以根据程序的运行情况确定要分配的堆内存的大小. 引用类型.值类型 1.将一个值类型变量赋给另一个值类型变量时,将复制包含的值.引用类型变量的赋值只复制对对象的引用,而不复

堆、栈、方法区、直接内存

堆(heap):FIFO,所有线程共享的一块内存,是专门存放对象实例的地方,GC发生在这里.随JVM启动而创建 栈(stack):LIFO,应该叫做虚拟机栈,每个线程私有的,存放该线程调用的方法栈帧,存储局部变量表.操作数栈.程序出口.动态链接等,每一个方法的调用就是入栈出栈的过程. 方法区(non-heap):存放已经加载过的类信息.静态变量.常量等不容易发生GC的信息,由各个线程共享(由静态变量或者常量可推知). 程序计数器:存放当前程序执行的字节码的行号指示器,如果执行的是native方法

这里想经过一个小程序研究标准库为 vector 对象提供的内存分配策,因为vector容器比list和deque容器用的很多,而且它的存储方式是连续的

我写一个简单的程序来区分vector容器size()和capacity()函数,这里capacity函数就是为vector容器预留了空间,不需要每次增添元素就要重新分配内存,这样效率上提高了很多,我通过一个间的小程序来研究,下面是程序和运行结果,比较简明可以看出capacity的大小都会比size大,因为size 指容器当前拥有的元素个数:而 capacity 则指容 器在必须分配新存储空间之前可以存储的元素总数.废话不多说,附上代码和运行结果:#include"stdafx.h" #

.NET的堆和栈04,对托管和非托管资源的垃圾回收以及内存分配

在" .NET的堆和栈01,基本概念.值类型内存分配"中,了解了"堆"和"栈"的基本概念,以及值类型的内存分配.我们知道:当执行一个方法的时候,值类型实例会在"栈"上分配内存,而引用类型实例会在"堆"上分配内存,当方法执行完毕,"栈"上的实例由操作系统自动释放,"堆"上的实例由.NET Framework的GC进行回收. 在" .NET的堆和栈02,值类型和

java堆和栈的区别

在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用. 堆内存用于存放由new创建的对象和数组.在堆中分配的内存,由java虚拟机自动垃圾回收器来管理.在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,在栈中的这个特殊的变量就变成了数组或者对象的引

Java当中的堆与栈详细解析

总结第一句话:Java语言使用内存的时候,栈内存主要保存以下内容:基本数据类型和对象的引用,而堆内存存储对象,栈内存的速度要快于堆内存.总结成一句话就是:引用在栈而对象在堆. Java疯狂讲义的一段对话作为开场白. 一个问题:为什么有栈内存和堆内存之分? 答:当一个方法执行时,每个方法都会简历自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也将自然销毁.因此,所有在方法中创建一个对象时,这个对象将被保存到运行时数据区中,以便利用(因为对象的创建成

Java中堆内存与栈内存分配浅析

Java把内存划分成两种:一种是栈内存,另一种是堆内存.在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,Java 会自动释放掉为该变量分配的内存空间,该内存空间可以立即被另作它用. 堆内存用来存放由 new 创建的对象和数组,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理.在堆中产生了一个数组或者对象之后,还可以在栈中定义一个特殊的变量,让栈中的这个变量的取值等于数

java中的堆和栈

Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用. 堆内存用于存放由new创建的对象和数组以及包装类.在堆中分配的内存,由java虚拟机自动垃圾回收器来管理.在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象

Java堆和栈的区别 - http://www.cnblogs.com/aishangJava/p/6897476.html

Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,Java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用. 堆内存用于存放由new创建的对象和数组.在堆中分配的内存,由java虚拟机自动垃圾回收器来管理.在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中