std::shared_ptr基本用法包括:
(1)取裸指针
//get()成员取回裸指针
std::shared_ptr <int> pa(new int(5));
int* p = pa.get(); /**< 取回裸指针 */
(2)判断是否为空
肯定可以这样写:
std::shared_ptr <int> pa;
if(pa.get() != nullptr)
...
或者更具C++风格的判断指针是否为空:
if(!pa.get())
...
以上都是通过get()取得裸指针,不过shared_ptr(其他智能指针也一样)重载了‘!’ 操作符。
对一个智能指针执行逻辑取反(‘!’)操作,当它所管理的裸指针为空时,返回真:
std::shared_ptr <int> pa;
if(!pa)...
也可以直接拿来和nullptr作相等比较,因为shared_ptr(其他智能指针也一样)重载了和指针的逻辑相等判断(‘==’)操作:
if(pa == nullptr)...
再试试逻辑不等判断操作‘!=’。默认空构造产生的shared_ptr拥有空的裸指针,即拥有一个nullptr。
std::shared_ptr <int> pb;
if(pb == nullptr) //和nullptr做相等比较
{cout << "pb的裸指针是空指针" << endl;
}
(3)比较操作
两个shared_ptr也可以用来比较,并且编译器能够帮我们杜绝风马牛不相及的比较
cout << "----------test5----------" << endl;
/**< 比较操作 */
std::shared_ptr <int> pi(new int);
std::shared_ptr <int> pi2(pi);//断定pi和pi2相等(所拥有的指针指向相同)
assert(pi2 == pi); //成立std::shared_ptr <char> pc(new char);if(pi == pc) //编译出错
比较pc和pi时,编译就不通过。一个指向int数据的指针,一个指向char数组的指针,二者有什么好比的呢?
为了模拟裸指针间的比较,shared_ptr也提供‘<’、‘>’、‘<=’及‘>=’等大小的比较,以及前面提到的‘==’和‘!=’。
(4)拷贝构造、赋值
std::shared_ptr <int> pa(new int);
std::shared_ptr <int> pb(pa); ///拷贝构造
std::shared_ptr <int> pc(new int);
pc = pb; ///赋值
002行,pb从pa复制并构造时,主要结果一是二者所拥有的裸指针指向同一对象内存 ;二是二者所共有“引用计数”是2。
003行,创建了pc,它拥有自己的裸指针(后成‘原裸指’)。此时它和pa、pb没有关系。
004行,pc改为从pb赋值,它将丢弃‘原裸指’,改为和pa、pb同指向。赋值时,‘原裸指’将被释放(因为没有别的shared_ptr拥有)。
严重危险的情况发生在下面这样的代码中:
int * pi = new int;
/**< 管理着共同的裸指针,而不是共同管理同一个裸指针 */
std::shared_ptr <int> sia(pi); //这没错
std::shared_ptr <int> sib(pi); //这?
sia的构造没有问题,构造之后的智能指针对象sia就托管了裸指针pi所指向的内存(因此后面不应该有delete pi的操作)。
关键是sib的构建,它的构造入参也是裸指针pi,而不是同类sia。这就造成了sia认为:我是第一个管理pi的shared_ptr;然而sib也认为自己是第一个管理pi的shared_ptr。
注意,此时sia和sib的状态是“它们管理着共同的裸指针”,而非我们所要的“它们共同管理同一个裸指针”,此时sia中的引用计数是1,而sib也一样,二者中任何一个结束生命周期,都会去释放pi所指向的内存。
【危险】:同一裸指针,确保从shared_ptr对象开始“分享”
做法很简单:如果你有一个裸指针需要使用shared_ptr管理,就确保一开始只用一个shared_ptr对象在创建时托管该裸指针,然后从第二个shared_ptr开始,就只从之前的shared_ptr拷贝构建。
“不作就不会死”
再看看以下“不作就不会死”的代码:
std::shared_ptr<int> sia(new int);
std::shared_ptr<int> sib(sia.get());
明明已经有一个智能指针sia了,可sib非要从sia身上得到裸指针然后再另立山头管理。
下面的代码犯了类似的问题
struct S2;struct S1
{S1(S2* ptr); ///构造入参是一个S2类型指针std::shared_ptr<S2> ps2;///成员数据
};struct S2
{S2();~S2();S1 * ps1; ///属性
};S1::S1(S2 * ptr): ps2(ptr) ///初始化共享指针ps2, ps2管理着指针ptr
{cout << "调用了S1构造函数,并将入参委托给shared_ptr ps2" << endl;
}
S2::S2()
{cout << "调用了S2构造函数,并以S2自身作为入参构造一个S1" << endl;ps1 = new S1(this);
}S2::~S2()
{delete ps1;
}void bad_test_1()
{S2 s2; ///s2是一个栈对象,
}void bad_test_2()
{cout << " new一个S2,委托给scoped_ptr " << endl;boost::scoped_ptr <S2> ps2 (new S2);
}
先看bad_test_1(),该函数创建一个S2的栈对象;于是看23行的S2的构造函数,发现它构造了一个S1的堆对象,并且以自身作为指针入参传递给S1的构造函数;继续跟踪,看S1构造函数,关注它取入参(一个指向某S2对象的指针)作什么事。
请看019行,这个指针被用来作为S1的成员数据ps2的构造入参,而ps2是一个share_ptr<S2>。
第一个问题出现了:ps2是一个智能指针,但它却管理了一个并不需要释放的栈对象,即bad_test_1()函数中s2.
bad_test_2()函数中创建的堆对象,已经托管给scoped_ptr <S2>,后面再七传八传最终又一次委托给某个shared_ptr<S2>。