栈里的对象
"构造完毕,请睁开眼吧!",迷迷糊糊中,听着这个声音,我睁开了眼睛,一位小哥映入眼帘。
“你是谁?我是谁?这又是什么地方?”,我的大脑一片空白。
“你是一个C++对象,这里是栈空间。我是创建你的线程,你先待着,一会儿会用到你,拜~”
“唉,等一下...”,还没等我回过神,小哥已经走远。
环顾四周,这个叫栈的地方,有好多好多的对象,大小不一,不远处还有一个巨无霸对象足足有好几千字节。
“哇!好大一个对象!”,我不禁发出了惊叹。
“一看就是没见过世面的,这才多小的个头,就在那大呼小叫的”,突然传来一个声音。
“谁在说话?”,我四处望去,却没发现动静。
“往哪看呢,没那么远,就在你旁边”,我这才注意到眼皮底下有个小个子在说话。
“你才8个字节的对象,好大的口气!”
“唉,纠正一下,我可不是对象哦,我只是一个指针,我指向的对象那才叫大呢!”
原来这小个子是个指针,顺着他指向的地址望去,果然有一庞然大物,虽不见其全身,估摸着至少也有几MB的大小。
“唉,你指向的对象为何不和我们在一块儿,我看那地方好像不属于栈的地界儿了”。
“说你没见过世面,你还不承认。咱们这栈空间有限,哪里装得下那么多大对象,他们那里叫堆区,一般有身份的对象都放在那边的。那里的对象都是通过new关键字生成的,你们可没这待遇哦,还有...”
“嘘!先别说话,你看线程小哥在干嘛?”,看到之前的小哥去了堆区,我打断了指针小个子的说话。
堆里的对象
只见小哥执行malloc函数在堆区划了一片空间,接着调用构造函数在那片空间上创建了一个对象。
“你刚才不是说那里的对象是new出来的吗,骗人!”
“这你都不知道,这个new = 内存分配 + 构造函数,而内存分配一般默认就是malloc,不懂还一惊一乍的,切~”,被指针小个子怼了回来,我真想找个缝躲起来。
正在我难为情的时候,线程小哥又来到了栈区,在我的头上不远处又创建了一个对象。
“你们好,我是新来的,请多指教!”
“欢迎欢迎,你好,请问你是?”,我第一个上前打招呼。
“咦,你手里怎么握着刚刚那个新对象的地址呢?你也是个指针吗,怎么跟我长得不一样”,小个子指针也凑了上来。
“这位老弟果然好眼力,我也确实是个指针,不过啊,你只是个裸指针,而我是个智能指针!”,新来这小子言语间透露着一股傲娇的味道。
“智能指针?这是个啥?”,小个子指针继续追问。
“哎哟,没想到见多识广的你也有不知道的事嘛!哈哈”,我趁此机会嘲笑了一把小个子。
智能指针
“好吧,那我就自我介绍下,我是一个shared_ptr,人类将我叫做智能指针,我手里握着一个裸指针,弥补了直接使用裸指针带来的烦恼!现在人类可喜欢用我们了。”
“烦恼?什么烦恼?”,小个子指针有点不太服气。
“当然是内存泄漏啦!像你们直接使用new创建出来的对象,程序员们要是忘记写delete释放,那这对象就成为被抛弃的孤儿,就会白白占用堆空间。一个两个还好,要是是在一个频繁执行的函数中发生这种事情,那堆空间就被这些抛弃的对象占满,那后果不堪设想!”,说完,智能指针的嘴角扬起了微笑。
“哦,还没完呢,裸指针还有个毛病就是谁来释放的问题,因为你们裸指针就是一个地址,可以在函数之间四处传递,最后传来传去,到底谁来释放,是不是还有别的函数和线程在使用都搞不清楚了,要是不释放呢,就内存泄漏了,要是别人还在用呢,释放了又会出问题,所以怎么着都很恼火”,说完,智能指针嘴角又上扬了许多。
小个子指针听完有些气馁,停顿一会之后再一次发出了疑问:“那你们是如何解决这个问题的呢?”
“我的内部有一个计数器,初始创建完对象后,把地址告诉了我,计数器值是1,以后每次把我赋值给别的智能指针,或者是函数传参拷贝到另一个shared_ptr,我的计数器值都会+1,表示又多了一个shared_ptr在使用它,相反的如果有一个shared_ptr对象析构了,那计数器值就-1。直到最后谁发现了这个计数器变为0,说明没人在用这个对象了,那就执行delete把它释放掉。看,是不是很聪明呐!”
“唉,我听明白了,原来你shared_ptr自己也是一个对象,利用自己的构造函数和析构函数来对一个计数器进行增加和减少操作来实现对裸指针指向对象的管理,是这样吧?”,我接上了这小子的话茬。
“对,没错,这个叫引用计数!我本身和你一样,也是一个对象哦,咱们这些在栈里的对象,有一个特别大的好处就是不用担心会内存泄漏,也不用担心析构函数不会被调用,编译器在生成指令的时候都已经自动做好了。”
智能指针的帮手
看着我俩打得火热,小个子指针不开心了,“有什么了不起,我能说出一种情况,就让你当场下不来台,哼!”
“哦,你倒是说说看?”
“如果两个对象A和B,A里面有个shared_ptr指向B,B里面有个shared_ptr指向A,也就是互相引用了,这样子,你们两个shared_ptr计数器永远是1,谁也释放不了,怎么样,没辙了吧!哈哈”,小个子一边说一边拍自己大腿,乐得不行。
我还在脑子里冥想这到底是个什么场景,智能指针开始回怼了:“你说的这种情况一早就想到了,在遇到互相引用的时候,我就要请出我的助手weaked_ptr了,它和我功能类似,最大的区别就是它不增加引用计数,只要A和B其中一方把shared_ptr换成weaked_ptr,就能打破你说的循环!”
小个子没料到这智能指针居然还有帮手,一下子语塞,最后实在没办法,憋出了一句:“你是厉害,可是很多现成的模块接口都是裸指针作为参数,要是用你肯定好多不兼容,还得我们裸指针上。”
“唉,此言差矣,既然是智能指针,这点困难都解决不了,哪有脸敢称智能两字?我们重载了->运算符和*运算符,让人类用起我们来和普通指针一样的体验。还提供了get()方法提供原始指针,在遇到你说的情况下也能用得上。怎么样,这下服了吧?唉,你别走啊,真小气~”
原来没等他说完,小个子指针已经灰溜溜的躲开了。
未完待续·······
彩蛋
“不好了,不好了”,小个子指针气喘吁吁的喊着。
“出什么事了?”
“你们看线程老哥在干嘛”
只见线程老哥执行memcpy函数,正在不断覆盖栈空间,智能指针那家伙已经遭殃,马上到我了·······
欲知后事如何,请关注后续精彩......
精彩回顾:
原文地址:https://www.cnblogs.com/xuanyuan/p/12283822.html