您的位置:首页 > 编程语言 > C语言/C++

[6]智能指针boost::shared_ptr

2015-01-17 21:50 387 查看
【1】boost::shared_ptr简介

boost::shared_ptr属于boost库,定义在namespace boost中,包含头文件#include<boost/shared_ptr.hpp>便可以使用。

boost::scoped_ptr中我们看到boost::scoped_ptr独享所有权,不允许赋值、拷贝。

而boost::shared_ptr是专门用于共享所有权的,由于要共享所有权,其在内部使用了引用计数机制。同时也就意味着支持赋值和拷贝。

boost::shared_ptr也是用于管理单个堆内存对象的。

【2】boost::shared_ptr详解

应用实例代码如下:

#include <iostream>
#include <boost/shared_ptr.hpp>

class Int
{
public:
Int(int nValue = 0)
{
m_nValue = nValue;
std::cout << "Constructor: " << m_nValue << std::endl;
}
~Int()
{
std::cout << "Destructor: " << m_nValue << std::endl;
}
void PrintValue()
{
std::cout << "PrintValue: " <<m_nValue<< std::endl;
}
void SetValue(int nSetValue)
{
m_nValue = nSetValue;
}

private:
int m_nValue;
};

void TestShared_Ptr(boost::shared_ptr<Int> spInt)
{  // 注意:无需使用 reference (或 const reference)
spInt->PrintValue();
std::cout << "TestShared_Ptr UseCount: " << spInt.use_count() << std::endl;
}

void TestShared_Ptr2()
{
boost::shared_ptr<Int> spInt(new Int(10));
if (spInt.get())
{
spInt->PrintValue();
spInt.get()->SetValue(20);
spInt->PrintValue();
(*spInt).SetValue(30);
spInt->PrintValue();
}

std::cout << "TestShared_Ptr2 UseCount: " << spInt.use_count() << std::endl;
TestShared_Ptr(spInt);
std::cout << "TestShared_Ptr2 UseCount: " << spInt.use_count() << std::endl;

//spInt.release();// 编译 error: 同样,shared_ptr也没有release函数
}

//执行结果如下:
/*
Constructor: 10
PrintValue: 10
PrintValue: 20
PrintValue: 30
TestShared_Ptr2 UseCount: 1
PrintValue: 30
TestShared_Ptr UseCount: 2
TestShared_Ptr2 UseCount: 1
Destructor: 30
*/
实例可见,boost::shared_ptr也可以很方便的使用。并且没有release()函数。

关键的一点,boost::shared_ptr内部维护了一个引用计数,由此可以支持复制、参数传递等。

boost::shared_ptr提供了一个函数use_count(),此函数返回 boost::shared_ptr内部的引用计数。

查看执行结果,我们可以看到在 TestShared_Ptr2函数中,引用计数为1,传递参数后(此处进行了一次复制),

在函数TestShared_Ptr内部,引用计数为2,在TestShared_Ptr返回后,引用计数又降低为1。

另外,由TestShared_Ptr2内创建智能指针对象,到函数结束再析构智能指针对象,确保释放掉内存资源。

当我们需要使用一个共享对象的时候,boost::shared_ptr是最佳选择。

此例也正体现了boost::shared_ptr是支持值语义,提供引用计数机制及RAII支持的智能指针。

【3】boost::shared_ptr总结

boost::shared_ptr的管理机制其实并不复杂,就是对所管理的对象进行了引用计数。

当新增一个boost::shared_ptr对该对象进行管理时,就将该对象的引用计数加一;

减少一个boost::shared_ptr对该对象进行管理时,就将该对象的引用计数减一;

如果该对象的引用计数为0的时候,说明没有任何指针对其管理,才调用delete释放其所占的内存。

boost::shared_ptr并不是绝对安全,下面几条规则能使我们更加安全的使用boost::shared_ptr:

1.避免对shared_ptr所管理的对象的直接内存管理操作,以免造成该对象的重释放

2.shared_ptr并不能对循环引用的对象内存自动管理(这点是其它各种引用计数管理内存方式的通病)。

3.不要构造一个临时的shared_ptr作为函数的参数。

如下列代码则可能导致内存泄漏:
void test()
{
fun(boost::shared_ptr<Int>(new Int()).g());
}
//正确的用法为:
void test()
{
boost::shared_ptr<Int> spInt(new Int());
fun(spInt.g());
}

当函数g()抛异常的时候就会泄露了,这个是boost文档上特地注明的标准bad Practices。

【4】boost::shared_ptr的实现机制

 boost::shared_ptr的实现机制其实比较简单,就是对指针引用的对象进行引用计数,当有一个新的boost::shared_ptr指针指向一个对象时,就把该对象的引用计数加1,减少一个boost::shared_ptr指针指向一个对象时,就把对该对象的引用计数减1。当一个对象的引用计数变为0时,就会自动调用其析构函数或者free掉相应的空间。

boost::shared_ptr的常用成员函数:

(1) 构造一个空的指针

shared_ptr(); // never throws
shared_ptr(std::nullptr_t); // never throws
template<class D> shared_ptr(std::nullptr_t p, D d);
template<class D, class A> shared_ptr(std::nullptr_t p, D d, A a);
上面几个函数可以初始化一个空的shared_ptr指针,其中,第三和第四个函数中的参数的意思是:d表示一个删除器(deleter),它会在释放资源的时候被调用,delete p会变成d(p)。a表示一个构造器,被用作分配空间。这两个接口允许调用者自己提供构造器和删除器,来自定义自己的构造和释放行为。

(2) 根据变量构造指针 
template<class Y> explicit shared_ptr(Y * p);
template<class Y, class D> shared_ptr(Y * p, D d);
template<class Y, class D, class A> shared_ptr(Y * p, D d, A a);
 这几个构造函数是通过一个Y类型的指针类型p来初始化shared_ptr指针,初始化后,指针会指针p所指的对象。其中,参数d和a的意义和上面相同。

(3) 拷贝构造函数

shared_ptr拥有常见的拷贝构造,移动构造函数,用法和普通构造函数一样,这里不做详述。还有一个比较特殊的构造函数:
template<class Y> shared_ptr(shared_ptr<Y> const & r, element_type * p); // never throws
这个函数的在boost的帮助文档中解释为: constructs a  shared_ptr  that  shares ownership  with  r  and stores  p(构造一个shared_ptr对象存储p并且与r共享所有权),这个构造函数被称为 aliasing constructor(不知道如何翻译,aliasing有重叠的意思)。r是将要共享所有权的指针,p是实际指向的对象,构造的指针调用get()或者operator->将返回p,而不是r。为了更好的理解这个函数,我们考虑shared_ptr指针对象由两个部分构成,一个是它的所有权(可以与其他指针共享的),另一个是它实际存储的对象。在普通应用中,这两部分是相同的。在由上述函数构造的shared_ptr中,两个部分是不同的。当一个指针的引用计数为0时,如果它还与其他指针共享所有权,那么它实际存储的对象不会被删除,直到共享的引用计数为0。下面的例子会更直观一些。
struct data {    ...};

struct object
{
data data_;
};

void f ()
{
shared_ptr<object> o (new object); // use_count == 1
shared_ptr<data> d (o, &o->data_); // use_count == 2
o.reset (); // use_count == 1
// When d goes out of scope, object is deleted.
}

void g ()
{
typedef std::vector<object> objects;

shared_ptr<objects> os (new objects); // use_count == 1
os->push_back (object ());
os->push_back (object ());

shared_ptr<object> o1 (os, &os->at (0)); // use_count == 2
shared_ptr<object> o2 (os, &os->at (1)); // use_count == 3

os.reset (); // use_count == 2

// When o1 goes out of scope, use_count becomes 1.
// When o2 goes out of scope, objects is deleted.
}
3. 使用 boost::shared_ptr的注意事项

(1)  不要把一个原生指针给多个shared_ptr管理 
int* ptr = new int;
boost::shared_ptr<int> p1(ptr);
boost::shared_ptr<int> p2(ptr);
这样做会导致ptr会被释放两次。在实际应用中,保证除了第一个shared_ptr使用ptr定义之外,后面的都采用p1来操作,就不会出现此类问题。

(2)  不要在函数实参里创建shared_ptr
function(shared_ptr<int>(new int), g());  //有缺陷
//可能的过程是先new int,然后调g(),g()发生异常,shared_ptr<int>没有创建,int内存泄露
//推荐写法
shared_ptr<int> p(new int());
f(p, g());
(3)  shared_ptr作为被保护的对象的成员时,小心因循环引用造成无法释放资源。

简单的例子:
class parent;
class children;
typedef boost::shared_ptr<parent> parent_ptr;
typedef boost::shared_ptr<children> children_ptr;

class parent {
public:
children_ptr children;
};

class children {public:
parent_ptr parent;
};
void test()
{
boost::shared_ptr<parent> father( new parent);
boost::shared_ptr<children> son(new children);
father->children = son;  //user_count() == 2
son->parent = father;  //user_count() == 2
}
在这个例子中,出现了循环引用计数,赋值后use_count()变为2,出函数后变为1,资源无法被释放。boost的解决方法是采用 weak_ptr来保存。
class parent {public:
boost::weak_ptr<children> children;
};

class children {public:
boost::weak_ptr<father> parent;
};
因为boost不会影响weak_ptr不会影响引用计数,不会造成循环引用计数。

  (4)  不要把this指针给shared_ptr

将this指针赋给shared_ptr会出现this指针被释放两次的危险,如下面的代码,会在t释放时析构一次,shared_ptr释放时析构一次。
class test {
public:
boost::shared_ptr<test> pget() {
return boost::shared_ptr<test>(this);
}
};

test t;
boost::shared_ptr<test> pt = t.pget();
boost库提供的解决方法是:使用enable_shared_from_this来实现。
class test : public boost::enable_shared_from_this<test> {
public:
boost::shared_ptr<test> pget() {
return shared_from_this();
}
};

test t;
boost::shared_ptr<test> pt = t.pget();
 4. std::tr1::shared_ptr和boost::shared_ptr

在新版本的C++标准中引用shared_ptr智能指针,名空间是std::tr1::shared_ptr。它和boost::shared_ptr的用法相同,在gcc4.3.x及以上的版本加选项 -std=gnu++0x即可使用。

【5】boost::shared_ptr简单实现

template<class T>
class shared_ptr {
private:
T* m_ptr;
unsigned int shared_count;

public:
shared_ptr(T* p):m_ptr(p),shared_count(1) { }
~shared_ptr() {
if (shared_count == 1)
{
delete m_ptr;
m_ptr = 0;
}
shared_count--;
}
T& operator*() { return *m_ptr; }
T* operator->() { return m_ptr; }

shared_ptr(shared_ptr& sp):m_ptr(sp.m_ptr),shared_count(sp.shared_count) {
shared_count++;
}

shared_ptr& operator = (shared_ptr& sp) {
sp.shared_count++;
if(shared_count == 1)
{
delete m_ptr;
m_ptr = 0;
}else
shared_count--;

m_ptr = sp.m_ptr;
shared_count = sp.shared_count;
return *this;
}
};

参考文章:
http://www.cnblogs.com/Braveliu/archive/2013/09/01/3295742.html http://blog.csdn.net/yusiguyuan/article/details/20076061
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++ 智能指针 boost