C++:从栈和堆来领略C#中的值范例和引用范例
副标题#e#
C++中并没有值范例和引用范例之说,尺度变量可能自界说工具的存取默认是没有区此外。但假如深入地来看,就要相识C++中,打点数据的两大内存区域:栈和堆。
栈(stack)是雷同于一个先进后出的抽屉。它的体积是有限的,一般为2M阁下。
而堆(heap)则相对来说体积可以很大,这一般跟计较机的虚拟内存配置有干系。
栈中存取工具的内存是自动接纳的,用完即销毁了,一般要领内部的变量和参数都是通过栈来存取的(但也正因为如此,它们的生命周期很短)。但它的问题是,体积有限。
一些大的工具,我们大概要通过堆来建设它。措施员可以节制这些工具什么时候建设,什么时候销毁。这无疑带来了机动性,也同时带来了一些风险,事实上,相当一部门的措施的瓦解都是因为不恰内地利用了堆,以及没有实时清理在堆上申请的内存。可能反过来说,大概会清理多次(这也会导致瓦解)。
凡是来说,假如但愿某个工具可能变量的生命更长一些,也可以将其作为全局变量可能静态变量。但那样又导致了它们必需比及措施竣事才会释放。
下面我用一个例子来演示一下这个问题
#include <iostream>
using namespace std;
class human{
public:
void Talk();
~human(){cout<<"析构函数在事情..."<<endl;}
private:
int age;
};
void human::Talk(){
cout<<"Hello"<<endl;
}
int main()
{
human h;//建设一个human工具,这个工具就保留在栈上,它所需的巨细是按照其成员抉择的
cout<<"h的巨细为:"<<sizeof(h)<<endl;
cout<<"h的地点是:"<<&h<<endl;
h.Talk();
human *p=new human();//通过new要害字,是在堆上面建设一个工具,它所申请的空间也是内部成员抉择的.这里也是4
cout<<"p的巨细为:"<<sizeof(p)<<endl;
cout<<"p的地点为:"<<p<<endl;
p->Talk();
delete p;
//删除p这个指针指向的堆上面的内存.假如用完该工具,我们不删除,那么该内存就一直存在,并不会自动删除.这就称为内存泄漏.
//假如范例界说了析构函数,此时将挪用它(反之,假如一个工具是在堆上建设的,那么除非挪用delete语句,不然析构函数不会运行
cout<<"p的地点为:"<<p<<endl;//我们只是删除了该块内存上面的数据,地点照旧存在的
//发起在删除p之后,将其置为0
//p=0;
//delete p;//但假如再次删除,又会产生瓦解,因为该内存已经没有了.
return 0;
}
#p#副标题#e#
有两句“析构函数在事情”,第一句是p这个指针所指向的堆上面谁人工具的析构,而最后那句在是h这个在栈上的工具的析构。
有乐趣的伴侣,可以将delete语句注释掉,则只会看到一个析构进程。
我们可以做得越发巨大一些来看
#include <iostream>
using namespace std;
class human{
public:
void Talk();
~human(){cout<<"析构函数在事情..."<<endl;}
private:
int age;
};
void human::Talk(){
cout<<"Hello"<<endl;
}
int main()
{
human h;//建设一个human工具,这个工具就保留在栈上,它所需的巨细是按照其成员抉择的
cout<<"h的巨细为:"<<sizeof(h)<<endl;
cout<<"h的地点是:"<<&h<<endl;
h.Talk();
human *p=new human();//通过new要害字,是在堆上面建设一个工具,它所申请的空间也是内部成员抉择的.这里也是4
cout<<"p的巨细为:"<<sizeof(p)<<endl;
cout<<"p的地点为:"<<p<<endl;
p->Talk();
human *p2=p;//建设别的一个指针,让他也生存一样的地点。
cout<<"p2的巨细为:"<<sizeof(p2)<<endl;
cout<<"p2的地点为:"<<p2<<endl;
p2->Talk();
delete p;
//删除p这个指针指向的堆上面的内存.假如用完该工具,我们不删除,那么该内存就一直存在,并不会自动删除.这就称为内存泄漏.
//假如范例界说了析构函数,此时将挪用它(反之,假如一个工具是在堆上建设的,那么除非挪用delete语句,不然析构函数不会运行
cout<<"p的地点为:"<<p<<endl;//我们只是删除了该块内存上面的数据,地点照旧存在的
//发起在删除p之后,将其置为0
//p=0;
//delete p;//但假如再次删除,又会产生瓦解,因为该内存已经没有了.
//delete p2;//不仅是p不能在删除,所以沟通的指针都不能再删除了,不然就瓦解了
return 0;
}
知道了上述道理之后,我们就不难领略C#中的值范例和引用范例了。
值范例是指基本数据范例(除了string),布局体,列举
引用范例是指类,接口,委托(其实雷同指针),string,object
#p#分页标题#e#
值范例是保留在栈上,长处是效率高,不需要特另外接纳。但它的空间是有限的,所有一般只适合基本范例(char,byte,int,short,long,bool,double,float等)。
引用范例则保留在堆上,但在栈上有一个指针(因为在堆上的工具都是匿名的,指针此时起到了一个体名的浸染,其实就是等同于引用的观念,固有引用之说)
引用范例既然保留在堆上,那么凭据C++的环境就必需我们本身去销毁它。但从上面的演示就不丢脸出,何时销毁它,以及会不会健忘销毁它,可能反复销毁它,都是一个很大的困难。所以在.NET Framework中,通过CLR中垃圾接纳器(GC)来认真接纳。一般的措施员不需要出格在意这个进程。
最后有意思的是,C#中的INT,其实并不等同于C++中的INT,各人假如有乐趣的话,可以看一下,它其实是一个布局体。