智能指针,就是利用对象的析构函数去执行资源释放,很久之前写的关于侵入式智能指针的笔记,今晚把它整理下。
资源共享型的智能指针有两种实现,一种是侵入式,一种是非侵入式。在教材里比较常见的是非侵入式的,它的实现完全放在智能指针模板里,模板类有一个用于保存资源类对象的指针变量,和一个用于记录资源对象使用计数的指针变量,这两个东西是所有的智能指针对象共享的,所以通过指针保存。而侵入式则不同,它的实现分散在智能指针模板和使用智能指针模板的类中:模板类只有一个用于保存对象的指针变量,对象的计数放在了资源类中。
非侵入式智能指针,它的引用计数变量为了保证所有对象共享,需要用堆里的内存,所以需要用new,这个都一样,不一样的是使用new的次数。侵入式智能指针的引用计数变量保存在对象里,因为对象是唯一的,所以引用计数也是唯一的,相比非侵入式智能指针,它的好处是:1、一个资源对象无论被多少个侵入式智能指针包含,从始至终只有一个引用计数变量,不需要在每一个使用智能指针对象的地方都new一个计数对象,这样子效率比较高,使用内存也比较少,也比较安全;2、因为引用计数存储在对象本身,所以在函数调用的时候可以直接传递资源对象地址,而不用担心引用计数值丢失(非侵入式智能指针对象的拷贝,必须带着智能指针模板,否则就会出现对象引用计数丢失)。坏处是:1、资源类必须有引用计数变量,并且该变量的增减可以被侵入式智能指针模板基类操作,这显得麻烦;2、如果该类并不想使用智能指针,它还是会带着引用计数变量。
另外,智能指针有一个无法避免的问题,就是循环引用。
最简单的例子,如:
class A { public: void attach(ptr< A > obj) { m_obj = obj; } private: ptr< A > m_obj; } void self_cir_ref() { ptr< A > pA( new A ); ///引用为1 pA->attach( pA ); ///attach函数,引用为2 } ///离开self_cir_ref(),引用减去1,还剩下1,资源泄漏。
还有例子,如:B类有A类对象,A类有B类对象,所以有了弱引用,上边的例子以及弱引用都跟侵入式智能指针无关,这里就不再偏题了。
侵入式智能指针的不完整简单例子:
#include <iostream> /** 侵入式智能指针,最简单的例子
cswuyg */ template< class T > class refObjectPtr { public: refObjectPtr(T* ptr) { m_pServer = ptr; if (m_pServer != 0) { m_pServer->addRef(); } } ~refObjectPtr() { if (m_pServer != 0) { m_pServer->release(); m_pServer = 0; } } T* operator->() const { return m_pServer; } private: T* m_pServer; }; class CTest { public: CTest(int value) : m_nValue(value) { m_pCount = new int(0); std::cout << __FUNCTION__ << std::endl; } ~CTest() { std::cout << __FUNCTION__ << std::endl; } void addRef() { (*m_pCount)++; } void release() { (*m_pCount)--; if (*m_pCount == 0) { delete this; } } int getCount() { return *m_pCount; } private: int* m_pCount; int m_nValue; }; void test2(CTest* ptr) { refObjectPtr<CTest> temp = ptr; //输出为2,输入参数是裸指针,因为引用计数就在对象本身,所以计数没有丢失 std::cout << temp->getCount() << std::endl; } void test1() { CTest* p = new CTest(10); refObjectPtr<CTest> ptr = p; test2(p); } int main(int argc, char* argv[]) { test1(); system("pause"); return 0; }
侵入式智能指针是值得应用的,避免了隐患,chromium里就大量使用了。