C#代码:
 private MyClass A; A = Function();
MyClass Function()
{
  MyClass B = new MyClass();
 
 
  //do something
  return B;                         //  ----------------(1)
}
本是以前是C++程序员,但在看到这样的C#代码时,不免有些疑惑。
首先, 在函数Function 的 “retrun B”执行后,对象B已超出了他的可视域,那么原则上“ MyClass B = new MyClass();”所申请的资源将在某个时候被收集掉。
但这时的 A 实际上是等于这个B的。因为A只是声明(没有“A = new  MyClass()”),那么我个人认为A与B实际上是一个完全一样的对象,(相当于C++的引用,而非附值),那么当B被收集掉后,则A就是个不完整的对象了,或是个错误的对象了。那么以后对A的操作就失去意义的。
请大伙给我解答一下,谢谢!!!!

解决方案 »

  1.   

    C++不太了解.
    在.NET下,A和B两个变量都是指象同一个对象,
    .NET在释放对象时是判断对象是否有变量在引用如果有就不会释放(对象在什么地方创建对生存期没直接影响).
    虽然B变量过了生存期,但对象本身还存在引用,所以是不会被释放的.
      

  2.   

    你的c++也没学好,想想重载+运算符,____没错,这段代码的道理同C++的指针没任何区别。
    如果懂C++,不可能理解不了这个
      

  3.   

    引用类型的对象只要有变量去引用他的话,那他就不可能回收的!
    CLR回收什么样的对象?
    是不在被任何变量引用的对象!
      

  4.   

    在c++中个人认为return B 返回的是个无效值,
    因为B这个临时变量超出了作用域,有没有析构函数而造成内存泄漏,
    而 A 引用的内存已经是垃圾了!
    这是C++容易出错的地方。
      

  5.   

    .NET在释放对象时是判断对象是否有变量在引用如果有就不会释放(对象在什么地方创建对生存期没直接影响).
    虽然B变量过了生存期,但对象本身还存在引用,所以是不会被释放的.我想这才是关键。
      

  6.   

    同意楼上的, 在 C++ 中这种写法是错误的, 将导致 A 引用了垃圾.但在 C# 中这是完全正确的, 并且是十分常见的写法, 因为在 C# 中任何正在使用的被引用的内存都不会被回收, 直到 A 超出作用域后, B 申请的内存才会被回收. C# 程序员再也不用象在 C/C++ 中那样担心引用了垃圾内存, 这也是 C# 的优点之一, 极大地方便了程序员. :)
      

  7.   

    没有被引用的内存会垃圾收集器自动回收的. 当然, 愿意的话, C# 程序员也可以显式地释放不再使用的资源. 最大的优点是, C# 程序员再也不用象在 C/C++ 中那样担心引用了垃圾内存.在 C# 中, 可以在一个方法中申请内存, 对申请来的这些内存作一些操作, 然后直接返回引用就可以了.而在 C/C++ 中, 必须在调用这个方法的主程序中申请内存. 如果是在方法中申请内存, 然后直接返回指针, 将导致引用了垃圾内存的严重后果.
      

  8.   

    不知道楼上几位在大学里到底有没有好好学过C++:(
    在C++中用new来创建的对象是在堆内存中的,指向它的指针必须用*的,所以没有
    MyClass B = new MyClass();这样的语法!
    而那句MyClass A;则是在栈空间中创建了一个完整的对象,而不是什么引用或指针,当然MyClass对象必须有默认构造函数。要在C++下通过编译,代码应该是如下所写的:
    MyClass* Function()
    {
      MyClass* B = new MyClass();
     
     
      //do something
      return B;                         //  ----------------(1)
    }而这样的代码,当这个函数出栈时,B所指向的对象是仍然存在的,因为它是在堆空间,而不是在栈空间!
    所以MyClass* A = Function();所指向的应该是一个实际存在的变量,不会指向什么垃圾的。
    当然,如果在C++写出这样的代码的话,由哪个函数来负责这个堆空间中对象的delete,是个需要考虑的问题。
      

  9.   

    我想造成你混淆的应该是这一句:
    MyClass B = new MyClass(); //c#中这么用在c#中类对象都是分配在堆上的,同时由于限制了指针的使用,
    所以你看不到这样的语句:MyClass * B = new MyClass();//c++中这么用所以你认为B对象会消亡
      

  10.   

    首先谢谢各位的帮助!
    这想这是C++程序员对C#的一个陌生的理解。同时对楼上有些结论不是很赞成。因为我想对与C#中这样的代码
    MyClass A;A = Function();MyClass Function()
    {
    MyClass B = new MyClass();

    retrun B;
    }其实这时的A什么都不是,因为它只是声明,而没有被构造。要被构造就必须有: A = new MyClass();语句。
    所以楼上有人临时对象一说站不住脚,因为A跟本就不是个正常的对象,它怎能等于一个对象呢?(对象的附值操作)
    如果非要拿临时对象来说,我想应该是这样的代码才较为真切:
    MyClass A = new MyClass();
    A = Function();
    MyClass Function()
    {
    MyClass B = new MyClass();

    retrun B;
    }这才与C++以下代码吻合:
    MyClass A
    A = Function();MyClass Function()
    {
    MyClass B;

    return B;
    }由于A实际上是没有分配空间的,所以只能把它当成类似为指针的东西了,实为引用(由于C++的引用定义时必须附值,这与C#由点区别)!
    那么在Function()内的对象B实为局部变量,如果在C++语言时它是会在return后,被析构掉,使A指向(引用)一个无效的内容,这是错误的。但反过来想,C#的对象不是在堆上创建的,那么就存在一个问题,C#是通过什么机制来分别一个对象是否已出作用域(说来话长,不知可否理解为C#跟本就没有局部变了,只是当它在没有引用的情况下由出了作用域,随时都可能变当作垃圾收掉,
    那么也在某个时候,就是出了作用域,在收集器还来不及收的时候,我也可能再引用成功)。所以,我初步认为在“.NET在释放对象时是判断对象是否有变量在引用如果有就不会释放(对象在什么地方创建对生存期没直接影响).虽然B变量过了生存期,但对象本身还存在引用,所以是不会被释放的.”的前提下,答案就一目了然了,问题也迎刃而解了。
      

  11.   

    能不能理解成 reutrn B是一个MyClass类型的复本,而不是本身的ref
      

  12.   

    就类似
    int a;
    a=getint();int getint()
    { int b = 2;
      return b;
    }
      

  13.   

    建议楼主去看一下《.NET Framework 程序设计》一书,上面有非常详细的叙述的。
    MyClass B = new MyClass();这行代码实际上是产生了两个:
    一个是在堆中产生的一个MyClass类型的对象,另一个是在栈中产生的指向堆中对象的对象(类似于指针)。函数返回值,调用函数中的A和被调用函数Fuction中的B都指向这个堆中的对象,之后,Fuction函数出栈作清理工作,把局部变量B弹出栈,此时堆中的对象被调用函数中的A引用着,所以GC在执行时不会将其回收。总结:函数返回后,变量B由于离开了作用域而被去除,但堆空间中的MyClass类型的对象由于变量A的引用,而仍然存在着。
      

  14.   

    同意高锰酸钾的见点。在C++中,new是在堆中分配空间。必须用delete释放。不会就会有内存泄露。
    同时。为了解决这个问题。Java跟.net才有了自动回收机制
      

  15.   

    是引用不是别的,C#中就是这样定义的,没有什么为什么,是编写C#语言的人这样定义的,和C++是不一样的,这种类比个人认为没有什么实际意义。
      

  16.   

    这段代码虽短,但是在C++和C#下却有很大的区别!要点:
    C#中的Class是引用类型,C++中的Class一般仍然值类型。
    时时记住这一点,比如看到代码中的=、return时都要记得这两者的区别。
    那么,整个代码也就好理解了。
    =================================================================
    private MyClass A;  //在C++中,A对象已经完成构造。而在C#中只不过作了声明 A = Function(); //在C++中这里的=是将一个对象赋给另一个对象,会用到拷贝构造。在C#中只不过多了一个引用
    MyClass Function()
    {
      MyClass B = new MyClass(); //在C++中一般不会有这等写法。理由参考前面。C#中就这么写
     
     
      //do something
      return B;       //在C++中,这会产生一个临时对象。在C#中已经没有这个概念。因为所有对象的管理都由CLR管理,无所谓临时不临时
    }
      

  17.   

    又仔细看了一下上边的代码和讨论,有人认为这段代码在C++下是错的。
    确实:MyClass B = new MyClass(); 根本就是语法错误。c++中的new 是用来手工开内存的,是一种高级的alloc,与delete对应。
    所以在C++中,要得到一个对象,只写MyClass B,就行了.然后在Function中可以对B作些更改。
    最好return B,都不会有明显的问题。但是整段性能很低其实,在C++中一样有引用可用只是要显示而已
      

  18.   

    最后return B,都不会有明显的问题。但是整段性能很低
      

  19.   

    呵呵,就算是c++这样的代码也没有问题啊。只不过new出来的指针需要在适当的时候delete,而C#是垃圾收集,就不用自己delete了,farmework会在适当的时候释放new出来的内存。
    其实就是这么简单。
      

  20.   

    C#中所有的Object都是引用(C++中的指针),用C++的眼光来看,Function在堆上分配了一个MyClass的实例对象,并返回指针给调用者。由于这个对象是在堆上的,所以当Function返回时,这个对象也一样有效。只不过,在C++中,调用者必须记得在不用的时候delete掉function返回的指针。初学时可以把C#看做不用delete的C++,呵呵,只管new,不管delete,framework会自动delete的。
      

  21.   

    如果你愣是要比较,C++使用new和dotNet中使用new 都是在堆中分配,只是dotNet固定在GCHeap中分配而在Windows平台下的C++就不确定可能是在本地dll/exe所在模块的堆也可能在动态堆中mscrt.dll中等等,根据你的编译选项来决定。同时C++和dotNet中new操作返回的是不同的东西,C++直接返回了内存中的指针,而dotNet中返回了一个Object代理相当于c++中的智能指针,当然它做了更多的工作如内存是可以移动的,这也是为什么你要直接操作C#中的内存需要fixed住它的原因。
      

  22.   

    我是这样理解的,不知道对不对.
    C#代码:
     private MyClass A;//旅客A对洒店的前台说:请给预定一间房,没有房间号也没给钥匙,只是打个招呼预定,具体房间到时再说; A = Function();//旅客B走了,但房间还在,前台说:你去住B刚才住的那间吧,并给他钥匙
    MyClass Function()
    {
      MyClass B = new MyClass();//旅客B对前台说:我要一间房,前台给出房间号并交给他钥匙,那间房就属于B的了
     
     
      //do something
      return B; //旅客B退房了,但房间还在,钥匙也交回前台
    }
    //最后旅客A也退房了,前台收回房间和钥匙,那房间不属于任何人,属于酒店(系统)了,等待下位客人入住
      

  23.   

    局部变量是不会扩大其作用域的,函数返回时会调用其拷贝构造创建具有新作用域的新对象,这点C++和C#是一样的,你C++也不怎么样。
      

  24.   

    堆中和栈中分配的内存都没搞清楚,这并不难理解的,学c语言的时候大家就应该清楚
    malloc分配的内存是需要手动回收的,并不是函数结束是自动回收,c++中的new,c#中的new 
    是一个道理
      

  25.   

    C++里函数返回的是该对象B的拷贝,虽然对象B随着函数的结束而结束了,但它的拷贝还存在,但是要是:
    MyClass A
    A = Function();MyClass Function()
    {
    MyClass B;

    return &B;
    }
    返回的是对象的引用,那么对象A指向的就是未定义的内存的别名。
      

  26.   

    #include <iostream.h>class a
    {
    public:
    a()
    {
    a1 = 100;
    cout<<"CONSTRUCT..."<<endl;
    }
    a(a& aIn)
    {
    cout<<aIn.a1<<","<<endl;
    aIn.a1 += 100;
    a1 = aIn.a1;
    cout<<"COPYING...\n"<<endl;
    } int a1;
    };
    a fun()
    {
    a pA;
    pA.a1 = 300;
    cout<<"pA:"<<&pA<<"\n"<<endl;
    return pA;
    }void main()
    {
    a pAMain = fun();

    cout<<"pAMain:"<<&pAMain<<"\na1:"<<pAMain.a1<<endl; 
    }整个过程产生了两次拷贝构造第一次是fun要退出时,把pA的值拷贝给临时对象作为返回值
    第二次是a pAMain = fun();中,临时对象的值被拷贝给pAMain 所以最后的值是500注意看,pA的地址是和pAMain 的地址完全不同的
      

  27.   

    eForm表单设计是一个页面设计工具,它既能完成静态HTML页面的设计,也能读写数据库完成动态页面设计。它采图形化可视化操作,所见即所得;通过拖拉即可完成表单设计。提供多种数据类型和Html控件,并预实现常见的业务逻辑功能(新增、删除、修改、查看、查询、报表、打印、图表等),用户无需编程或是修改代码,完全在界面上操作即可设计完页面;也能写一些事件的代码以完成复杂表单的设计。 
         eForm表单设计实际上就是一个构件库,它对大量实用的控件进行了结构化的封装和调试。通过简单的拖拉设置就可以实现控件所能实现的功能,从而大大减少了代码的编写和调试难度. webprint使用简单,灵活.能满足绝大多数页面打印的需要.它内含一个在vc7.0上开发的ATL小控件(只有59k),这个小控件主要实现对IE浏览器中文档打印格式的控制,可以定制打印纸型,纸张来源,打印方向,设置表头,表尾,表格,表格列宽,打印预览,分页,缩放等等用户经常关心的属性。 webprint使用户通过脚本可以控制自定义纸张,打印方向,页边距等等属性达到定制打印的目的,这些定制属性的设置不会改变IE浏览器的默认打印机属性。也可以通过服务器端的页面调用WebPrint生成客户端的页面达到设置打印参数的目的。
     
     
      

  28.   

    为什么说楼主c++没学好呢??
    因为
    private MyClass A; A = Function();
    MyClass Function()
    {
      MyClass B = new MyClass();
     
     
      //do something
      return B;                         //  ----------------(1)
    }
    这段代码在c++下如果处理得当也没问题
    首先
    MyClass B=new MyClass();
    这句c++一般不是这样
    应该是
    MyClass B;
    或者
    MyClass* B = new MyClass;
    ===========================
    首先讨论第一种,
    return B ;
    结束后是会超出B的作用域,而引起B的消失,但
    A = Function();会调用 MyClass的copy construct ,不会让 A指向一个已经
    结束的类
    这里需要一个技巧如果MyClass包含指针变量,最好手工加一个copy construct ,
    MyClass( const MyClass& source )
    {}
    ===================================
    第二种,
    B是建立在heap上的,如果不手动的delete,还会有内存泄露的危险,