《白话C++》第10章 STL和boost,Page85 std::shared_ptr常用功能

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>。

  • 18
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值