智能指针用法

Posted stingliang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了智能指针用法相关的知识,希望对你有一定的参考价值。

“独占型”指针unique_ptr

unique_ptr 指针指向的堆内存无法同其它 unique_ptr 共享,每个 unique_ptr 指针都独自拥有对其所指堆内存空间的所有权,被定义在头文件<memory>中,并且使用std命名空间

构造unique_ptr

空构造
std::unique_ptr<int> p1();
std::unique_ptr<int> p2(nullptr);
接管已有指针
std::unique_ptr<int> p3(new int);

由此就创建出了一个 p3 智能指针,其指向的是可容纳 1 个整数的堆存储空间

移动构造
std::unique_ptr<int> p4(new int);
// std::unique_ptr<int> p5(p4);//错误,堆内存不共享
std::unique_ptr<int> p5(std::move(p4));//正确,调用移动构造函数

对于调用移动构造函数的 p4 和 p5 来说,p5 将获取 p4 所指堆空间的所有权,而 p4 将变成空指针(nullptr)

功能函数

release()

释放当前 unique_ptr 指针对所指堆内存的所有权,但该存储空间并不会被销毁

reset(p)

其中p表示一个普通指针,如果p为nullptr,则当前unique_ptr也变成空指针;反之,则该函数会释放当前unique_ptr指针指向的堆内存(如果有),然后获取p所指堆内存的所有权(p为nullptr

工厂函数

在C++11标准库中,并没有提供unique_ptr的make_unique工厂函数(C++14提供了),但是在boost库中,对其的工厂函数也做了补充

C++14
auto p = std::make_unique<int>(10);
assert(p && *p == 10);
使用boost库中的make_unique

首先需要包含头文件<boost/smart_ptr/make_unique.hpp>,使用命名空间boost

#include <boost/smart_ptr/make_unique.hpp>

auto p = boost::make_unique<int>(10);
assert(p && *p == 10);

定制删除器

默认情况下,unique_ptr指针采用std::default_delete<T>方法释放堆内存,也可以自定义符合实际场景的释放规则,和 shared_ptr 指针不同,为 unique_ptr 自定义释放规则,只能采用函数对象的方式。例如:
//自定义的释放规则

struct myDel
{
    void operator()(int *p) {
        delete p;
    }
};
std::unique_ptr<int, myDel> p6(new int, myDel());

“共享”指针shared_ptr

boost库中的shared_ptr,被收纳为C++新标准中的shared_ptr,可以自由地拷贝和赋值,当没有代码在使用它时,才会自行析构。

基本概念

引用计数:顾名思义,该指针被引用的次数

构造shared_ptr

空构造
shared_ptr() // 创建一个空智能指针,其原始指针就是nullptr
接管原始指针
shared_ptr(T * p) // 从已有的指向T类型地指针p处接管内存,同时引用计数加一
接管智能指针
shared_ptr(shared_ptr conster&r) operator= 
// 从另一个智能指针构造,引用计数加一,相当于和原来的shared_ptr共同管理一块内存
指定析构函数的智能指针
shared_ptr(T * p, D d) 
// 其构造过程类似于(2),但是使用了D d作为指定的析构函数,在后面章节会讲到

功能函数

reset()

将引用计数减一。它也可以带参数,参数类型和构造函数相似,相当于先将引用计数减一,再去接管另一个指针

unique()和use_count()

unique()在shared_ptr是指针的唯一所有者时为true,use_count()返回当前指针的引用计数

工厂函数make_shared

make_shared()函数可以接收若干个参数,如何传递给类型T的构造函数(在构造对象时很有用),然后创建一个shared_ptr<T>的对象并返回,通常使用工厂函数创建对象比直接创建shared_ptr对象的方式更快且更高效

定制删除器

shared_ptr(Y*p,Dd)的第一个参数是要被管理的指针,它的含义与其他构造函数的参数相同。而第二个参数则告诉shared_ptr在析构时不要使用delete来操作指针p,而要用d来操作,即把deletep换成d(p)

在这里删除器d可以是一个函数对象,也可以是一个函数指针,只要它能够像函数那样被调用,使得d(p)成立即可。对删除器的要求是它必须可拷贝,其行为也必须像delete那样,不能抛出异常。为了配合删除器的工作,shared_ptr提供一个自由函数get_deleter(),它能够返回内部的删除器指针。有了删除器的概念,我们就可以用shared_ptr实现管理任意资源。只要这种资源提供了它自己的释放操作,shared_ptr就能够保证它自动释放。

举例:假设有一组文件操作函数,使用file_t:

class file_t{...}; 
// open file 
file_t * open_file() 
{    
    cout << "open file" <<endl;    
    ...    
    return new file_t; 
} 
// close file 
void close_file(file_t * s) 
{    
    cout << "close file" <<endl;    
    ... 
}

那么对文件指针的释放操作就应该是close_file(),而不是delete,在这里,删除器close_socket()是一个自由函数,因此只需要把函数名传递给shared_ptr即可。也可以在函数名前加上取地址操作符“&”,其效果是等价的:

file_t * s = open_socket(); 
// 直接传入删除器 
shared_ptr<file_t> p(s, &close_file); 
// 等价操作 
// shared_ptr<file_t> p(s, &close_file);

用法示例

shared_ptr被包含在

#include <boost/smart_ptr.hpp> 
using namespace boost;

shared_ptr()可以在任何情况下接手new的分配结果,其也提供基本的线程安全访问,可以被多个线程安全地读取

// 接管原始指针
shared_ptr<int> sp(new int(10));
// 此时shared_ptr是指针唯一的额持有者
assert(sp.unique());

// 拷贝构造
shared_ptr<int> sp1(sp);
// shared_ptr<int> sp1 = sp;
assert(sp == sp1 && sp.use_count() == 2);

// 使用解引用操作符操作被指对象
*sp1 = 100;

// 停止使用shared_ptr
sp.reset();

// 使用工厂函数构造
auto sp2 = make_shared<string>("make shared");
auto sp3 = make_shared<vector<int>>(10, 2);
assert(sp3 -> size() == 10);

“弱”指针weak_ptr

因为它不具有普通指针的行为,没有重载operator*->。它的最大作用在于协助shared_ptr工作,它只负责观测资源的使用情况。

构造函数

weak_ptr被设计为与shared_ptr协同工作,它的构造和构造不会使shared_ptr的引用计数增加或减少,它可以从一个shared_ptr或者weak_ptr构造

shared_ptr<int> sp(new int(10));
assert(sp.use_count() == 1);

weak_ptr<int> wp(sp);
assert(wp.use_count() == 1);
assert(!wp.empty());

功能函数

weak_ptr没有重载operator*->,因为它不共享指针,不能操作资源,这正是它“弱”的原因。但它可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象,把“弱”关系转换为“强”关系,从而操作资源。但当表示指针是否有效的expired()==true时,lock()函数将返回一个存储空指针的shared_ptr。

// 接上例
if(!wp.expired())
{
    // 获得一个shared_ptr
    shared_ptr<int> sp2 = wp.lock();
    *sp2 = 100;
    assert(wp.use_count() == 2);
}

assert(wp.use_count() == 1);
// 智能指针失效
sp.reset();
assert(wp.expired());
// weak_ptr将获得一个空指针
assert(!wp.lock());

对象自我管理

weak_ptr的一个重要用途是获得this指针的shared_ptr,使对象自己能够生产shared_ptr管理自己:对象使用weak_ptr观测this指针,这并不影响引用计数,在需要的时候就调用lock()函数,返回一个符合要求的shared_ptr供外界使用。
这个解决方案是一种惯用法,在头文件<boost/enable_shared_from_this.hpp>里定义一个助手类enable_shared_from_this<T>,它的声明摘要如下:

template<class T>
class enable_shared_from_this    // 辅助类,需要继承使用
{
public:
    // 工厂函数,产生this指针的shared_ptr
    shared_ptr<T> shared_from_this();
};

使用weak_ptr的时候只需要让想被shared_ptr管理的类继承它即可,成员函数shared_from_this()会返回this指针的shared_ptr。例如:

class self_shared: public enable_shared_from_this<self_shared>
{
public:
    self_shared(int n):num(n){}
    int num;
    void print()
    {
        cout << "self_shared:" << num << endl;
    }
};

int main()
{
    auto sp = make_shared<self_shared>(313);
    sp ->  print();
    
    auto p = sp -> shared_from_this();
    p -> num = 100;
    p -> print();
}

【注意事项】不能对一个未被shared_ptr管理的对象使用shared_from_this操作获取shared_ptr,否则会产生未定义的错误。

// 错误用法
self_shared ss;
auto p = ss.shared_from_this();

以上是关于智能指针用法的主要内容,如果未能解决你的问题,请参考以下文章

浅谈ObjectARX智能指针AcDbObjectPointer的用法

智能指针unique_ptr用法

智能指针用法

C++ 智能指针 - 全部用法详解

“智能指针”的成长

C++ 智能指针 shared_ptrmake_shared用法