C++11智能指针用法

    科技2022-08-17  109

    shared_ptr和unique_ptr都支持的操作:

    *p p->mem p.get() 返回p中保存的指针,智能指针p在某些情况下可以代替p.get(),比如cout<<p 输出内部指针 p.reset() 放弃内部对象的所有权,等价于 p = nullptr p.reset(T *ptr) 变更拥有的对象 在shared中,reset会造成引用计数的减少,为0时删除;unique中直接删除 swap(p, q) 交换p和q中的指针

    shared_ptr

    创建

    shared_ptr<T> p1; //默认初始化,保存空指针 shared_ptr<T> p1(T* ptr); //构造函数,示例如下 shared_ptr<T> p1(new T()); shared_ptr<T> p2 = make_shared<T>(args); shared_ptr<T> p3(p2); //拷贝构造,会增加p2中的计数器 //管理数组,自动调用delete[]而不是delete shared_ptr<T[]> up(new T[10]); //不使用原本的delete释放操作,自定义删除器: shared_ptr<T> p3(..., deleterFun); //定制void(T*)类型删除器函数,或者函数对象或者lambda表达式 //将std::unique_ptr作为std::shared_ptr的移动构造参数 std::shared_ptr<std::string> shared = std::make_unique<std::string>("test"); std::shared_ptr<std::string> shared = std::move(unique);

    不能将普通指针直接赋值给智能指针。

    操作

    p = q p和q都是shared_ptr,所保存的指针必须能相互转换。递减p的引用计数,若为0则释放;递增q的引用计数。 p.unique() 返回是否是独占所有权 p.use_count() 返回引用计数

    注意事项

    不要使用同一个原始指针构造 shared_ptr:

    int *num = new int(23); std::shared_ptr<int> p1(num); std::shared_ptr<int> p2(p1); // 正确使用方法 std::shared_ptr<int> p3(num); // 不推荐 std::cout << "p1 Reference = " << p1.use_count() << std::endl; // 输出 2 std::cout << "p2 Reference = " << p2.use_count() << std::endl; // 输出 2 std::cout << "p3 Reference = " << p3.use_count() << std::endl; // 输出 1

    虽然p1 p3指向相同对象,但并没有统一,当p1超出作用域时会调用delete释放num内存,此时num成了悬空指针,当p3超出作用域再次delete的时候就可能会出错。

    weak_ptr

    weak_ptr 是为了配合shared_ptr而引入的一种智能指针,对被 shared_ptr 管理的对象存在非拥有性(“弱”)引用,无法直接访问,也不会影响引用计数。在访问所引用的对象前必须先转换为 shared_ptr。

    weak_ptr<T> w 默认构造,空指针 weak_ptr<T> w(p) p为shared或weak,与p指向相同对象。T必须能转换成p指向的类型 w = p 同上 w.reset() 将w置为空 w.use_count() w监视对象的引用计数 w.expired() w指向的对象引用计数是否为0,是为true w.lock() 如果expired为true,返回空shared_ptr;否则返回一个指向w的shared_ptr

    weak_ptr 用来表达临时所有权的概念:当某个对象只有存在时才需要被访问,而且随时可能被他人删除时,可以使用 weak_ptr 来跟踪该对象。需要获得临时所有权时,则将其转换为 shared_ptr(计数+1)。

    if(shared_ptr<int> np = wp.lock()){ //如果np不为空则条件成立,计数暂时+1 // 在if中,np与p共享对象 } //退出if函数体,计数-1恢复

    weak_ptr 的另一用法是打断 shared_ptr 所管理的对象组成的环状引用。若这种环被孤立(例如无指向环中的外部共享指针),则 shared_ptr 引用计数无法抵达零,而内存被泄露。能令环中的指针之一为弱指针以避免此情况。

    unique_ptr

    unique_ptr<T> u1 //默认构造,保存空指针 unique_ptr<T> up(T *ptr) unique_ptr<T> up(new T) //自定义删除器 unique_ptr<T, D> u2 //空指针,用一个类型为D的可调用对象来代替delete unique_ptr<T, D> u2(d) //空指针,用类型为D的对象d代替delete unique_ptr<T, D> u2(new T) unique_ptr<T, D> u2(new T, d) unique_ptr<T, decltype(deleterFun)*> u(new T, deleterFun) //使用删除器函数,lambda同理 unique_ptr<T> up = make_unique<T>(args) //C++14起用 u.release() //返回指针,放弃控制权(并不释放内存),将u置空,不同于reset

    shared_ptr的自定义删除器不同于shared_ptr,两者分别代表两种不同的删除器绑定方式: 1.shared_ptr在运行时绑定删除器,删除器保存为一个指针,可以随时通过reset来更改删除器类型,更灵活; 2.unique_ptr在编译时绑定,因为删除器的类型是unique_ptr类型的一部分,更高效。

    ——《C++primer 5th》P599

    自定义删除器的具体写法:https://blog.csdn.net/DumpDoctorWang/article/details/88598015

    unique_ptr独占指向的对象,不能拷贝或赋值unique_ptr,但可以通过调用release或reset转移指针所有权:

    unique_ptr<A> p(new A); unique_ptr<A> p2(p.release()); unique_ptr<A> p3; p3.reset(p2.release());

    传递unique_ptr参数和返回unique_ptr

    不能拷贝unique_ptr的规则有一个例外:我们可以拷贝或赋值一个将要被销毁的unique_ptr。

    unique_ptr<int> clone(int p){ unique_ptr<int> ret(new int(p)); //... return ret; }

    编译器知道要返回的对象将要被销毁。在此情况下,编译器执行移动赋值。

    智能指针线程安全

    引用计数是atomic,读是安全,多线程写不安全。 https://blog.csdn.net/solstice/article/details/8547547

    参考: https://zh.cppreference.com/w/cpp/memory/unique_ptr https://blog.csdn.net/DumpDoctorWang/article/details/88598015 https://blog.csdn.net/shaosunrise/article/details/85228823?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param 《C++primer 5th》

    Processed: 0.012, SQL: 9