《白话C++》第10章 STL和boost,Page67~70 std::auto_ptr

std::auto_ptr可以不经意间转移裸指针控制权

std::auto_ptr持有裸指针的控制权,却可以随随便便看似不经意地转移给另一个auto_ptr:

#include <iostream>
#include <memory>

using namespace std;

struct S
{
    int a;

    void SetA(int a)
    {
        this->a = a;
    }

    ~S()
    {
        cout << "~S: bye-bye" << endl;
    }
};

void test_auto_ptr_crash()
{
    auto_ptr <S> aps(new S);
    auto_ptr <S> aps2(aps); //转移对裸指针的所有权
    aps2->SetA(99);
    aps->SetA(100); //有可能造成程序挂掉
}

023行,aps获得并负责管理一个新的裸指针(通过new S所得);

024行,看是普通的“拷贝构造”操作,实际确实在做转移操作,aps拱手让出的原是它拥有且负责管理的裸指针

025行,aps2拥有,并可以控制该裸指针了

026行,“旧人”aps还想操作裸指针,但此时它拥有的裸指针是“nullptr”。

auto_ptr作为入参,传给函数:

再看看复杂点的情况,比如将auto_ptr作为入参,传给函数:

void foo(auto_ptr<S> aps)
{
    ;
}

void call_foo()
{
    auto_ptr <S> aps(p);
    /**< 当实参传递时,需要复制,这一复制,
    原auto_ptr即失去了原有的裸指针 */
    foo(aps);
    cout << "===============" << endl;

    aps->SetA(100);//程序通常要挂掉……
}

演示auto_ptr作为入参:

foo函数的入参是一个auto_ptr,而不是“auto_ptr <S>&”或“auto_ptr<S>*”,因此当实参传递时,需要复制,这一复制,原auto_ptr即失去了原有的裸指针,那就试试将入参改成auto_ptr的引用:

入参改为auto_ptr的引用:

void foo2(auto_ptr<S> & aps)
{
    ;
}

这将传递,没有发生裸指针转移所有权的事,但进入函数之后,万一又有复制的需要:

void foo2(auto_ptr<S> & aps)
{
    //如果有复制,还是会被转移走的。
    auto_ptr<S> aps2(aps);
}

以引用方式传递auto_ptr给某个函数,调用者更加没有安全感了,因为调用处原auto_ptr到底会不会被“转移”走,调用者完全不可控。

常量引用:

如果一个函数明确不准备转移auto_ptr入参的所有权,解决方法是“常量引用”:

/**< 明确不准备转移auto_ptr入参的所有权,解决方法是“常量引用” */
void foo3(auto_ptr <S> const& aps)
{
    auto_ptr <S> aps2(aps); //编译不过去
    aps.release(); //也编译不过去

    //delete aps.get();//代码阻止不了某些程序员的猥琐
}

foo3函数作者感到迷惑了:我只是想以“aps”为模子复制一份,为什么编译不能过呢?

auto_ptr本身也感到委屈:“我早就说过了,我的‘复制’就是转移,是你们记不住呀”

auto_ptr类模板的拷贝构造函数:

典型的拷贝构造auto_ptr的拷贝构造

class XXX

{

public:

        ...

        XXX(XXX const& other);

        ...

};

template <typename T>

class auto_ptr

{

public:

        ...

        auto_ptr(auto_ptr& other)

        ...

};

对比两边的构造函数声明,左边入参带有const修饰,右边的没有。auto_ptr没有欺骗我们,它特意声明拷贝构造时的入参不能是常量,原因就在于它复制other时,还会修改other的内容,这是合法的,因为C++没规定拷贝构造的入参一定是const。只是这违反了直觉,违反了“拷贝构造”的语义。所以C++标准委员会决定抛弃auto_ptr。

auto_ptr作为函数返回值:

auto_ptr作为函数返回值的做法,在旧代码中很常见。

std::auto_ptr <S> CreateS()
{
    ...
    S* ps = new S;
    ...

    return std::auto_ptr <S> (ps);
}

这个做法挺灵活的:如果调用者就不想理会什么智能指针,那完全还可以使用裸指针接盘:

//得到智能指针管理的指针,原智能指针不再占有内存
S* sp = CreateS().release();
//按照常规方式使用,包括负责释放
delete sp; 

如果调用者认同智能指针有一定的方便些,那就保留auto_ptr接下:

//保留auto_ptr,以方便自动释放
std::auto_ptr <S> asp = CreateS(); 

如果调用了CreateS()函数却不理会其返回值:

CreateS();//不理会返回值,反正它会被自动释放……

如果代码这么写:

S* sp = CreateS().get(); //可怕

这样写,本意上应该是想达到第一种写法的效果,但后果相当可怕。为什么?

因为用get()函数得到的裸指针后,智能指针依旧管理着裸指针,如果裸指针使用delete删除后,会导致智能指针失效。

  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值