面试——关于引用计数

强引用、弱引用

强引用影响对象的引用计数,进而影响对象的生存期。弱引用不影响对象的引用计数,在功能上类似于普通指针,但是弱引用能检测到所引用的对象是否已经被释放,从而避免非法访问内存。

循环引用

循环引用导致内存泄漏是基于引用计数进行GC的基本问题,一般来说避免这种问题有几种可行的方法:
(1)当只剩下最后一个引用的时候需要手动打破循环引用释放对象
(2)当A的生存期超过B的生存期的时候,B改为使用一个普通指针指向A
(3)使用弱引用的智能指针打破循环引用
虽然这几种方法都可行,但1、2需要程序员手动控制,麻烦且容易出错,所以我们一般使用第三种方法,例如weak_ptr。
当然了也可以使用其他不基于引用计数的GC技术,例如追踪式GC(通过扫描系统中是否有到对象的引用判断对象是否存活)

侵入式 vs 非侵入式

侵入式
侵入式的引用计数管理要求资源对象本身维护引用计数,同时提供增减引用计数的管理接口。通常侵入式方案会提供配套的侵入式引用计数智能指针。该智能指针通过调用资源对象的引用计数管理接口来自动增减引用计数。COM对象与CComPtr便是侵入式引用计数的一个典型实例。

非侵入式
非侵入式的引用计数管理对资源对象本身没有任何要求,而是完全借助非侵入式引用计数智能指针在资源对象外部维护独立的引用计数。shared_ptr便是基于这个思路。

对比
侵入式的优点:
1、一个资源对象无论被多少个侵入式智能指针包含,从始至终只有一个引用计数变量,不需要在每一个使用智能指针对象的地方都new一个计数对象,这样子效率比较高,使用内存也比较少,也比较安全;
2、因为引用计数存储在对象本身,所以在函数调用的时候可以直接传递资源对象地址,而不用担心引用计数值丢失(非侵入式智能指针对象的拷贝,必须带着智能指针模板,否则就会出现对象引用计数丢失)。
侵入式的缺点:
1、资源类必须有引用计数变量,并且该变量的增减可以被侵入式智能指针模板基类操作,这显得麻烦;
2、如果该类并不想使用智能指针,它还是会带着引用计数变量。

shared_ptr是否线程安全

(1) 虽然引用计数部分使用了原子操作进行修改,但shared_ptr的操作包括对象指针的赋值与引用计数的修改两部分,所以不是线程安全的;
(2)引用计数部分使用原子操作,是为了保证引用计数本身不会在多线程操作时在修改后出现错误的值;
(3)假如要实现线程安全的引用计数智能指针,需要保证对象指针的赋值与引用计数的修改两部分操作完成的原子性,可以加锁或者使用侵入式引用计数;

enable_shared_from_this

由于shared_ptr是非侵入式引用计数,通常情况下只能通过智能指针来修改引用计数,无法在对象内部影响到引用计数。为了满足在对象内部修改引用计数的情况(例如,绑定一个异步回调函数到该对象,此时需要保证回调回来前该对象不析构),从而有了enable_shared_from_this,可以从对象内部获取自己的shared_ptr

你可能感兴趣的:(面试——关于引用计数)