C++ : 从栈和堆来理解C#中的值类型和引用类型
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这个指针所指向的堆上面那个对象的析构,而最后那句在是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
值类型是生存在栈上,好处是效率高,不需要额外的回收。但它的空间是有限的,所有一般只适合基础类型(char,byte,int,short,long,bool,double,float等)。
引用类型则生存在堆上,但在栈上有一个指针(因为在堆上的对象都是匿名的,指针此时起到了一个别名的作用,其实就是等同于引用的概念,固有引用之说)
引用类型既然生存在堆上,那么按照C++的情况就必须我们自己去销毁它。但从上面的演示就不难看出,何时销毁它,以及会不会忘记销毁它,或者重复销毁它,都是一个很大的难题。所以在.NET Framework中,通过CLR中垃圾回收器(GC)来负责回收。一般的程序员不需要特别在意这个过程。
最后有意思的是,C#中的INT,其实并不等同于C++中的INT,大家如果有兴趣的话,可以看一下,它其实是一个结构体。
![]()
C++ : 从栈和堆来理解C#中的值类型和引用类型的更多相关文章
- 【译】.NET中六个重要的概念:栈、堆、值类型、引用类型、装箱和拆箱
为何要翻译 一来是为了感受国外优秀技术社区知名博主的高质量文章,二来是为了复习对.NET技术的基础拾遗达到温故知新的效果,最后也是为了锻炼一下自己的英文读写能力.因为是首次翻译英文文章(哎,原谅我这个 ...
- .NET中六个重要的概念:栈、堆、值类型、引用类型、装箱和拆箱 (转)
作者: Edison Chou 来源: 博客园 发布时间: 2014-09-03 15:59 阅读: 318 次 推荐: 2 原文链接 [收藏] 原文作者:Shivprasad k ...
- .NET中的六个重要概念:栈、堆、值类型、引用类型、装箱和拆箱
为何要翻译 一来是为了感受国外优秀技术社区知名博主的高质量文章,二来是为了复习对.NET技术的基础拾遗达到温故知新的效果,最后也是为了锻炼一下自己的英文读写能力.因为是首次翻译英文文章(哎,原谅我这个 ...
- [转] .NET中六个重要的概念:栈、堆、值类型、引用类型、装箱和拆箱
为何要转载 一来是最近面试了几家公司,发现问的还都是这些的基础知识,二来是为了复习对.NET技术的基础拾遗达到温故知新的效果. 为什么有人说,不动笔不读书.我现在也是深有体会了,看过的东西不一定会记得 ...
- C#栈、堆的理解(2)
接上一遍博文有关值类型和引用类型的相关概念. 所有值类型数据存放:栈(内存) 引用类型的数据存放:堆(内存) 栈:可以认为是一本书的目录部分称其为栈.栈可快速检索,运行速度比堆大,而且栈的空间小得多. ...
- 图解C#的值类型,引用类型,栈,堆,ref,out
C# 的类型系统可分为两种类型,一是值类型,一是引用类型,这个每个C#程序员都了解.还有托管堆,栈,ref,out等等概念也是每个C#程序员都会接触到的概念,也是C#程序员面试经常考到的知识,随便搜搜 ...
- C#基础知识系列二(值类型和引用类型、可空类型、堆和栈、装箱和拆箱)
前言 之前对几个没什么理解,只是简单的用过可空类型,也是知道怎么用,至于为什么,还真不太清楚,通过整理本文章学到了很多知识,也许对于以后的各种代码优化都有好处. 本文的重点就是:值类型直接存储其值,引 ...
- 值类型,引用类型,栈,堆,ref,out
在网上收集... C#的值类型,引用类型,栈,堆,ref,out C# 的类型系统可分为两种类型,一是值类型,一是引用类型,这个每个C#程序员都了解.还有托管堆,栈,ref,out等等概念也是每个C# ...
- .NET六大剑客:栈、堆、值类型、引用类型、装箱和拆箱
.NET六大剑客:栈.堆.值类型.引用类型.装箱和拆箱 一.“堆”,“栈”专区 这两个字我相信大家太熟悉了,甚至于米饭是什么?不知道...“堆”,“栈”是什么?哦,这个知道... 之前我也写过一篇堆栈 ...
随机推荐
- Python学习笔记——进阶篇【第九周】———MYSQL操作
Mysql 增删改查操作 查看数据库 show databases; 创建数据库并允许中文插入 create database s12day9 charset utf8; 使用数据库 use s12d ...
- Python调用(运行)外部程序
在Python中可以方便地使用os模块运行其他的脚本或者程序,这样就可以在脚本中直接使用其他脚本,或者程序提供的功能,而不必再次编写实现该功能的代码.为了更好地控制运行的进程,可以使用win32pro ...
- chrome手动添加拓展
https://www.crx4chrome.com/crx/978/ Free Download Postman REST Client CRX 0.8.4.19 for ------------- ...
- 关于:1.指针与对象;2.深浅拷贝(复制);3.可变与不可变对象;4.copy与mutableCopy的一些理解
最近对深浅拷贝(复制)做了一些研究,在此将自己的理解写下来,希望对大家有所帮助.本人尚处在摸索阶段,希望各位予以指正. 本文包括如下方向的探索: 1.指针与对象: 2.深/浅拷贝(复制): 3.可变/ ...
- 如何占用你用户的时间 and 如何提高客户的满意度 。 待续
未来的商业竞争, 可能本质上是在争取客户的时间 嗯..有不定时, 未知的奖励,游戏行业就经常使用, 比如打怪掉装备, 不一定掉什么好东西, 让人充满了期待, 玛雅宝石, 有一定的概率... 觉得公司员 ...
- dao代码模板
提供数据源以及回收资源的工具类DbUtils: public class DbUtils { private static ComboPooledDataSource dataSource = new ...
- FOUNDATION_EXPORT 延伸
一般iOS我们定义常量的方法有两种,来看下面例子 我的.h文件 1 2 FOUNDATION_EXPORT NSString * const kMyConstantString; FOUNDATI ...
- redirect-action
功能: 点击login , redirect 到hello.jsp 显示 "hello" 点击redirect, redirect 到 error.jsp 显示 "err ...
- ES 集群调整、升级 最佳实践
日常应用中我们会经常对es 集群做一些参数调整或者升级版本,但是每次关闭节点再打开 其中的数据同步的痛苦估计有很多人领悟过(有可能出现IO或者网络拥堵导致恶性循环)官网有套方案可以尝试一下: 1.关掉 ...
- msyql sql语句
参考: http://www.cnblogs.com/aspnethot/articles/1397130.html 修改表字段ALTER TABLE table_name CHANGE old_fi ...