ual things.
综合来说,shared_ptr 具有可以共享和转移所有权,可以被标准库的容器所使用 ,线程安全的,不能指向一块动态增长的内存(用share_array代替)等特点。
(4)举例:在容器中使用share_ptr
在许多容器类包括标准STL容器中,都需要复制操作(inserting an existing element into a list, vector, or container)。然而,当这种复制操作很复杂或者难以实现可用的时候,指针容器是一种简单有效的解决方式。例如下面的例子:
1: std::vector vec; 2: vec.push_back( new CMyLargeClass("bigString") );
上面这个程序将内存管理任务的交给其调用者,这里我们可以使用share_ptr来改写它,
1: typedef boost::shared_ptr CMyLargeClassPtr; 2: std::vector vec; 3: vec.push_back( CMyLargeClassPtr(new CMyLargeClass("bigString")) );
这样改写后对任务的内存管理就非常简单了,当容器被destroyed,其中的元素也随之自动的destroyed。
但是,如果还有其他智能指针在引用它,则引用的那个元素依然存在,而不被释放掉。如下程序,
1: void Sample3_Container() 2: { 3: typedef boost::shared_ptr CSamplePtr; 4: 5: // (A) create a container of CSample pointers: 6: std::vector vec; 7: 8: // (B) add three elements 9: vec.push_back(CSamplePtr(new CSample)); 10: vec.push_back(CSamplePtr(new CSample)); 11: vec.push_back(CSamplePtr(new CSample)); 12: 13: // (C) "keep" a pointer to the second: 14: CSamplePtr anElement = vec[1]; 15: 16: // (D) destroy the vector: 17: vec.clear(); 18: 19: // (E) the second element still exists 20: anElement->Use(); 21: printf("done. cleanup is automatic\n"); 22: 23: // (F) anElement goes out of scope, deleting the last CSample instance 24: }
(5)使用share_ptr需要注意的地方
1. shared_ptr多次引用同一数据,如下:
1: { 2: int* pInt = new int[100]; 3: boost::shared_ptr sp1(pInt); 4: // 一些其它代码之后… 5: boost::shared_ptr sp2(pInt); 6: }
这种情况在实际中是很容易发生的,结果也是非常致命的,它会导致两次释放同一块内存,而破坏堆。
2. 使用shared_ptr包装this指针带来的问题,如下:
1: class tester 2: { 3: public: 4: tester() 5: ~tester() 6: { 7: std::cout << "析构函数被调用!\n"; 8: } 9: public: 10: boost::shared_ptr sget() 11: { 12: return boost::shared_ptr(this); 13: } 14: }; 15: int main() 16: { 17: tester t; 18: boost::shared_ptr sp = t.sget(); // … 19: return 0; 20: }
也将导致两次释放t对象破坏堆栈,一次是出栈时析构,一次就是shared_ptr析构。若有这种需要,可以使用下面代码。
1: class tester : public boost::enable_shared_from_this 2: { 3: public: 4: tester() 5: ~tester() 6: { 7: std::cout << "析构函数被调用!\n"; 8: } 9: public: 10: boost::shared_ptr sget() 11: { 12: return shared_from_this(); 13: } 14: }; 15: int main() 16: { 17: boost::shared_ptr sp(new tester); 18: // 正确使用sp 指针。 19: sp->sget(); 20: return 0; 21: }
3. shared_ptr循环引用导致内存泄露,代码如下:
1: class parent; 2: class child; 3: typedef boost::shared_ptr parent_ptr; 4: typedef boost::shared_ptr child_ptr; 5: class parent 6: { 7: public: 8: ~parent() { 9: std::cout <<"父类析构函数被调用.\n"; 10: } 11: public: 12: child_ptr children; 13: }; 14: class child 15: { 16: public: 17: ~child() { 18: std::cout <<"子类析构函数被调用.\n"; 19: } 20: public: 21: parent_ptr parent; 22: }; 23: int main() 24: { 25: parent_ptr father(new parent()); 26: child_ptr son(new child); 27: // 父子互相引用。 28: father->children = son; 29: son->parent = father; 30: return 0; 31: }
如上代码,将在程序退出前,father的引用计数为2,son的计数也为2,退出时,shared_ptr所作操作就是简单的将计数减1,如果为0则释放,显然,这个情况下,引用计数不为0,于是造成father和son所指向的内存得不到释放,导致内存泄露。
4. 在多线程程序中使用shared_ptr应注意的问题。代码如下:
1: class tester 2: { 3: public: 4: tester() {} 5: ~tester() {} 6: // 更多的函数定义… 7: }; 8: void fun(boost::shared_ptr sp) 9: { 10: // !!!在这大量使用sp指针. 11: boost::shared_ptr tmp = sp; 12: } 13: int main() 14: { 15: boost::shared_ptr sp1(new tester); 16: // 开启两个线程,并将智能指针传入使用。 17: boost::thread t1(boost::bind(&fun, sp1)); 18: boost::thread t2(boost::bind(&fun, sp1)); 19: t1.join(); 20: t2.join(); 21: return 0; 22: }
这个代码带来的问题很显然,由