zoukankan      html  css  js  c++  java
  • 浅谈面向对象语言中对象的使用

          为了避免本文误导大家,首先声明:在面向对象语言中探讨这些没有太大的意义,但是它可以帮助我们更好的理解.NET语言特性。本文以C#为例,会涉及.NET中的克隆(浅复制)。

           关于这个讨论,是由合作开发引起的。当时在DBHelper层里使用了new关键字创建一个数据库表对象(DataTable),而我在数据访问层(DAL)接收数据库表对象时没有加关键字new,只是声明了一个类型为DataTable的“对象变量”:DataTable dt;而没有这样声明:DataTable dt = new DataTable();究竟这样写对不对呢?

           废话不多说,直接说原理:地球人都知道,要想实例化一个对象,必须用关键字new,例如:DataTable dt = new DataTable();。这个new就起到一个分配内存的作用,当编译器读到关键字new时,便在内存中开辟一块空间,用来放置类的实例,而dt中存放的仅仅是这块内存空间的首地址,存放的并不是对象,一个对象怎么可能存在一个变量里?其实,这个dt默认是int32型的,它存放的仅仅是一个地址,也可以说成指针。但是如果我们不用new,直接DataTable dt;,这样仅仅是声明了一个变量,他的类型为DataTable,说明这个变量可以容纳DataTable实例的首地址,但实际上dt仅仅是一个int32型的变量。所以类似:

     

    DataTable dt1 = new DataTable();//实例化类
    DataTable dt2; //定义一个对象,但是不实例化
    dt2 = dt1; //把dt1中存放的实例的首地址,传给dt2

    这样的写法,我们发现dt1和dt2都可以完美的完成功能,表面上看有两个实例,但实际上只有一个实例!dt2只不过是引用了dt1中存放的实例地址!dt1和dt2在使用同一个实例,使用同一块内存地址。所以,它们是互相影响的,假如dt1被强制销毁,那么dt2也无法正常使用(有兴趣的读者可以自己试一下,在这就不举例了)。其实,我们可以很不正规的说:对象是指内存中的某片区域,而并不是你定义的“对象变量”。

           如果你学过C语言,能轻易理解我上边所说的。但是又有人说:这其实就是.NET中的克隆---浅复制。这是大错特错的!当时我也没仔细想,就冒然认同了,但后来仔细一想,发现这根本是两个概念。克隆(浅复制)的定义:“创建当前对象的浅表副本,方法是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象”。至此,读者可以发现,克隆实际上是创建了一个“不完整的对象”,和上边所讲的引用地址根本不是一回事。克隆首先就创建了一个新对象,也就是已经开辟了内存空间,然后,把被克隆对象中数值类型的字段(堆栈数据,可以简单的理解为数值类型局部变量)复制到新对象的内存空间,对于被克隆对象中的引用类型字段(也就是对象),只复制引用(复制其所在的内存地址),并不开辟内存空间,这个原理和上边讲的是一致的。简单的说:克隆复制了数值型数据,引用了对象。而上边提到的写法是完全引用

           为了更加具有说服力,让代码运行结果说明一切:

    clsTestB类:

     class clsTestB
        {
            private string strByVal;//数值型字段
            public string byval
            {
                get { return strByVal; }
                set { strByVal = value; }
            }
        }

    clsTestA类,实现了克隆接口:

    class clsTestA:ICloneable
        {
            private string strByVal; //数值型字段
            private clsTestB clstestb = new clsTestB(); //引用型字段
            //byval属性访问数值型字段
            public string byval
            {
                get { return strByVal; }
                set { strByVal = value; }
            }
            //byref属性访问引用型字段
            public string byref
            {
                get { return clstestb.byval; }
                set { clstestb.byval = value; }
            }
            //实现克隆接口
            public Object Clone()
            {
                return (Object)this.MemberwiseClone();
            }
        }


    完全引用示例代码:

    clsTestA clstest1 = new clsTestA(); //创建clsTestA类新实例
    clsTestA clstest2;   //定义clsTestA类“对象变量”
    clstest1.byval = "0"; //设定clstest1数值属性初始值
    clstest1.byref = "0"; //设定clstest1引用属性初始值
    clstest2 = clstest1;// clstest2完全引用clstest1指向的地址,并没有创建新实例
    clstest1.byval = "byval";//改变clstest1数值属性初始值
    clstest1.byref = "byref";//改变clstest1引用属性初始值
    MessageBox.Show(clstest2.byval + "|" + clstest2.byref);   
    

    这段代码输出结果为:byval|byref。分析:clstest2引用clstest1后,clstest1改变了数值属性和引用属性,clstest2的相应属性也随之改变,说明无论是数值型字段还是引用型字段,clstest2完全引用clstest1和clstest2其实就是一个东西。


    克隆示例代码:

    clsTestA clstest1 = new clsTestA(); //创建clsTestA类新实例
    clsTestA clstest2;   //定义clsTestA类“对象变量”
    clstest1.byval = "0"; //设定clstest1数值属性初始值
    clstest1.byref = "0"; //设定clstest1引用属性初始值
    clstest2 = (clsTestA)clstest1.Clone();//clstest1通过克隆对clstest2赋值
    clstest1.byval = "byval";//改变clstest1数值属性初始值
    clstest1.byref = "byref";//改变clstest1引用属性初始值
    MessageBox.Show(clstest2.byval + "|" + clstest2.byref);   

    这段代码输出结果为:0|byref。分析:clstest2是通过clstest1克隆而来的,clstest2已经是一个新实例,clstest2中的数值型属性由clstest1中复制过来,已经不再受clstest1影响;而clstest2中的引用型属性是直接引用的clstest1,所以当clstest1中的引用属性改变时,会影响clstest2中的相应属性。

          综上所述,我们甚至还可以这样写:

    DataTable dt1 = new DataTable();//实例化类
    DataTable dt2; //定义一个对象,但是不实例化
    DataTable dt3; //定义一个对象,但是不实例化
    dt2 = dt1; //把dt1中存放的实例的首地址,传给dt2
    dt3 = dt2; //把dt2中存放的实例的首地址,传给dt3

          这样的写法完全是可以的!只要实例化一次,以后即使不实例化,也可以直接引用。有人可能会有疑问:当我们使用dt3时,dt1被销毁了怎么办?您尽管放心,.NET的托管机制是不会让一个正在被引用的内存地址释放的。dt1、dt2、dt3有任何一个“对象”正在被使用,他们所指向的地址就不会被释放。(如果想了解为什么不会被释放,请参阅《Windows核心编程》

          当然,我们要养成一个良好的习惯,实例化对象都要加new。毕竟我们使用的是面向对象的语言,而不是底层语言,不必过多考虑这些问题,实例化对象的时候记得加上一个new,保证不会出问题!

  • 相关阅读:
    [转]怎么看工作是否到位
    [转]一个合格程序员该做的事情——你做好了吗?
    深入图解虚拟机(一)--一个问题引出的思考
    正则表达式边用边学(一)——分组、捕获
    redhat无法注册RHN的解决办法
    使用jquery扩展表格行合并方法探究
    扩展jquery easyui datagrid编辑单元格
    js点滴知识(1) -- 获取DOM对象和编码
    使用雅虎YUI Compressor压缩JS过程心得记录
    插曲一--记《数据结构与问题求解(Java语言版)(第4版)》翻译问题
  • 原文地址:https://www.cnblogs.com/iyangyuan/p/2801848.html
Copyright © 2011-2022 走看看