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的操作就失去意义的。
请大伙给我解答一下,谢谢!!!!
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的操作就失去意义的。
请大伙给我解答一下,谢谢!!!!
在.NET下,A和B两个变量都是指象同一个对象,
.NET在释放对象时是判断对象是否有变量在引用如果有就不会释放(对象在什么地方创建对生存期没直接影响).
虽然B变量过了生存期,但对象本身还存在引用,所以是不会被释放的.
如果懂C++,不可能理解不了这个
CLR回收什么样的对象?
是不在被任何变量引用的对象!
因为B这个临时变量超出了作用域,有没有析构函数而造成内存泄漏,
而 A 引用的内存已经是垃圾了!
这是C++容易出错的地方。
虽然B变量过了生存期,但对象本身还存在引用,所以是不会被释放的.我想这才是关键。
在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,是个需要考虑的问题。
MyClass B = new MyClass(); //c#中这么用在c#中类对象都是分配在堆上的,同时由于限制了指针的使用,
所以你看不到这样的语句:MyClass * B = new MyClass();//c++中这么用所以你认为B对象会消亡
这想这是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变量过了生存期,但对象本身还存在引用,所以是不会被释放的.”的前提下,答案就一目了然了,问题也迎刃而解了。
int a;
a=getint();int getint()
{ int b = 2;
return b;
}
MyClass B = new MyClass();这行代码实际上是产生了两个:
一个是在堆中产生的一个MyClass类型的对象,另一个是在栈中产生的指向堆中对象的对象(类似于指针)。函数返回值,调用函数中的A和被调用函数Fuction中的B都指向这个堆中的对象,之后,Fuction函数出栈作清理工作,把局部变量B弹出栈,此时堆中的对象被调用函数中的A引用着,所以GC在执行时不会将其回收。总结:函数返回后,变量B由于离开了作用域而被去除,但堆空间中的MyClass类型的对象由于变量A的引用,而仍然存在着。
同时。为了解决这个问题。Java跟.net才有了自动回收机制
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管理,无所谓临时不临时
}
确实:MyClass B = new MyClass(); 根本就是语法错误。c++中的new 是用来手工开内存的,是一种高级的alloc,与delete对应。
所以在C++中,要得到一个对象,只写MyClass B,就行了.然后在Function中可以对B作些更改。
最好return B,都不会有明显的问题。但是整段性能很低其实,在C++中一样有引用可用只是要显示而已
其实就是这么简单。
C#代码:
private MyClass A;//旅客A对洒店的前台说:请给预定一间房,没有房间号也没给钥匙,只是打个招呼预定,具体房间到时再说; A = Function();//旅客B走了,但房间还在,前台说:你去住B刚才住的那间吧,并给他钥匙
MyClass Function()
{
MyClass B = new MyClass();//旅客B对前台说:我要一间房,前台给出房间号并交给他钥匙,那间房就属于B的了
//do something
return B; //旅客B退房了,但房间还在,钥匙也交回前台
}
//最后旅客A也退房了,前台收回房间和钥匙,那房间不属于任何人,属于酒店(系统)了,等待下位客人入住
malloc分配的内存是需要手动回收的,并不是函数结束是自动回收,c++中的new,c#中的new
是一个道理
MyClass A
A = Function();MyClass Function()
{
MyClass B;
return &B;
}
返回的是对象的引用,那么对象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 的地址完全不同的
eForm表单设计实际上就是一个构件库,它对大量实用的控件进行了结构化的封装和调试。通过简单的拖拉设置就可以实现控件所能实现的功能,从而大大减少了代码的编写和调试难度. webprint使用简单,灵活.能满足绝大多数页面打印的需要.它内含一个在vc7.0上开发的ATL小控件(只有59k),这个小控件主要实现对IE浏览器中文档打印格式的控制,可以定制打印纸型,纸张来源,打印方向,设置表头,表尾,表格,表格列宽,打印预览,分页,缩放等等用户经常关心的属性。 webprint使用户通过脚本可以控制自定义纸张,打印方向,页边距等等属性达到定制打印的目的,这些定制属性的设置不会改变IE浏览器的默认打印机属性。也可以通过服务器端的页面调用WebPrint生成客户端的页面达到设置打印参数的目的。
因为
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,还会有内存泄露的危险,