C++11 智能指针

来源:互联网 发布:淘宝需要生产许可证 编辑:程序博客网 时间:2024/06/16 15:51

C++11标准库中提供的智能指针主要有:std::auto_ptr, std::unique_ptr, std::shared_ptr。std::auto_ptr在C++11中已经标记为弃用,而在C++17中就完全移除了,不建议使用std::auto_ptr。
标准库提供的所有智能指针都在<memory>头文件中。

#include <memory>using namespace std;

现有如下测试类:

class Apple {public:    Apple() {        id_ = 0;        cout << "Apple()" << endl;    }    Apple(int id) {        id_ = id;        cout << "Apple(" << id_ << ")" << endl;    }    ~Apple() {        cout << "~Apple()" << endl;    }    void SetId(int id) {        id_ = id;    }    int GetId() {        return id_;    }private:    int id_;};

一、 std::auto_ptr

std::auto_ptr在C++11中虽然已经弃用,但还可以使用;但在C++17中就完全移除了。
std::auto_ptr在自身被销毁时释放相应的对象。

int main(){    {        auto_ptr<Apple> apple1(new Apple(1));    } // 超出apple1的作用域,apple1被销毁,其指针指向的对象被析构。    return 0;}

std::auto_ptr支持赋值构造和复制赋值,但右值会失去指针的所有权,如:

auto_ptr<Apple> apple0(new Apple(1));auto_ptr<Apple> apple1 = apple0;apple0.id();  // 引发异常

上面的代码中,apple0赋值给了apple1,此时apple1拥有指针的所有权,而apple0中的指针就是NULL了。
所以说,std::auto_ptr虽然可以复制,但涉及到指针所有权的转义,所以std::auto_ptr不能用在标准容器(如std::vector等)中。

二、 std::unique_ptr

2.1 使用unique_ptr

std::unique_ptr和std::auto_ptr一样,也是在自身被销毁时释放相应的对象。std::unique_ptr被设计用来取代std::auto_ptr.
std::unique_ptr支持普通指针的*,->操作符,但不支持普通指针的++, --算术操作运算符。

std::unique_ptr**不**支持赋值构造和复制赋值;不支持使用=赋值语法将一个普通的指针当作初始值:

unique_ptr<Apple> apple = new Apple(2); // 错误unique_ptr<Apple> apple(new Apple(2));  // 正确
int main() {    {        unique_ptr<Apple> apple2(new Apple(2));        unique_ptr<Apple> apple3 = apple2; // 编译失败    } // 超出apple2的作用域,apple2被销毁,其指针指向的对象被析构。}

2.2 make_unique

可以使用std::make_unique来构造unique_ptr:

std::unique_ptr<Apple> apple = std::make_unique<Apple>(1);

2.3 所有权

std::unique_ptr不必一定拥有对象,它可以是empty。例如它被默认构造函数创建出来时:

unique_ptr<Apple> apple;

也可以调用reset()或对它赋予nullptr,来让它不拥有对象。
此外,可以使用release方法来返回一个指向被管理对象的指针,并释放所有权。

2.4 检查是否拥有对象

可以调用empty()来检查unique_ptr是否拥有对象;而且unique_ptr重载了bool()操作符,也可以直接将其转为bool类型来判断。

unique_ptr<Apple> apple;if(apple.empty()) {}if(apple) {}

2.5 所有权转义

虽然std::unique_ptr不支持支持赋值构造和复制赋值,但其要实现类似std::auto_ptr的指针所有权转义,可以通过使用swapstd::move来实现,因为std::unique_ptr实现了转移构造函数转移赋值操作符重载:

int main() {    {        unique_ptr<Apple> apple2(new Apple(2));        unique_ptr<Apple> apple3;        apple2.swap(apple3);     // apple2的指针从此无效        /*        或者:        unique_ptr<Apple> apple3 = std::move(apple2);  // apple2指针从此无效        */    } // 超出apple2的作用域,apple2被销毁,其指针指向的对象被析构。}

2.6 处理Array

默认情况下unique_ptr如果失去所有权(也许因为被销毁,或因为被赋予新值,或因为变成empty),会为其所拥有的对象调用delete。不幸的是C++无法区分指针是“指向单对象”还是“指向数组”。而C++语言又规定,对于数组应该使用delete[]而不是delete。所以下面的语句是错误的:

unique_ptr<Apple> apples(new Apple[10]);  // 错误

为了处理数组,C++标准库为unique_ptr提供了一个偏特化版本(partial specialization)来处理数组,在失去对象的所有权时,对该对象调用delete[]。

unique_ptr<Apple[]> apples(new Apple[10]);

但是,这个偏特化的版本不在提供操作符*,->的支持,改而提供操作符[]的支持,用来访问其所指向的数组中的某一个对象:

unique_ptr<Apple[]> apples(new Apple[10]);  // OKapples[0].SetId(0);   // OK

2.7 自定义deleter

当所指向的对象要求不只是调用delete或delete[]来释放对象时,就需要指定自己的deleter。unique_ptr的deleter可以是类、函数指针。
如果deleter是类,则该类必须提供操作符()的重载,参数必须是指向对象的指针,如下:

class AppleDeleter {public:    void operator()(Apple* p) {        std::cout << "will delete Apple object" << std::endl;        p->SetId(0);        delete p;    }};int main(){    std::unique_ptr<Apple, AppleDeleter> apple(new Apple(1));    apple.reset();    return 0;}

如果deleter是函数指针,则函数指针为void(*)(T*)std::function<void(T*)>,如下:

void AppleDeleter(Apple* p) {    std::cout << "will delete Apple object" << std::endl;    p->SetId(0);    delete p;}int main(){    std::unique_ptr<Apple, void(*)(Apple*)> apple(new Apple(1), AppleDeleter);    apple.reset();    return 0;}

可以使用typedef定义一个中介的类型定义式:

typedef void(*PFNAppleDeleter)(Apple*);void AppleDeleter(Apple* p) {    std::cout << "will delete Apple object" << std::endl;    p->SetId(0);    delete p;}int main(){    std::unique_ptr<Apple, PFNAppleDeleter> apple(new Apple(1), AppleDeleter);    apple.reset();    return 0;}

三、 std::shared_ptr

3.1 使用shared_ptr

std::shared_ptr和boost中的shared_ptr类似,当对象的引用计数为0时,对象会被释放。
std::shared_ptr支持赋值构造和复制赋值,左值和右值同时有效,引用计数会加1。

int main(){    {        shared_ptr<Apple> apple3(new Apple(3));        {            shared_ptr<Apple> apple4 = apple3;        }    } // 此时Apple对象才会析构。    return 0;}

你可以先声明shared_ptr,然后对它赋值一个指针。然而不可以直接使用=赋值操作符,必须改用reset()

std::shared_ptr<Apple> apple;apple.reset(new Apple(1));

3.2 std::make_shared

可以使用std::make_shared构造shared_ptr:

auto sp = std::make_shared<Apple>(12);

3.3 处理Array

前面介绍的std::unique_ptr在处理数组时,可以使用偏特化的版本,但std::shared_ptr没有提供用于处理数组的偏特化的版本。所以shared_ptr要处理数组,必须自定义deleter.

std::shared_ptr的deleter的定义和std::unique_ptr一样,但在使用deleter时,std::shared_ptr不需要再模板参数中提供deleter定义,如:

void AppleDeleter(Apple* p) {    std::cout << "will delete Apple object" << std::endl;    delete []p;}int main(){    // 模板参数只有一个    std::shared_ptr<Apple> apples(new Apple[10], AppleDeleter);    apples.reset();    return 0;}
原创粉丝点击