Loading...
墨滴

公众号:offer多多

2021/07/31  阅读:164  主题:橙心

使用 C++ 智能指针遇到的坑

使用 C++ 智能指针遇到的坑

阅读收益

  • 智能指针目的就是代替原始指针,那么问题来了,原始指针都可以用智能指针代替吗?不能

  • 智能指针 unique_ptr、shared_ptr 和 weak_ptr三个,那么问题来了 一个不能代替全部吗?不能

  • shared_ptr vs weak_ptr

shared_ptr 使用条件: 有多个使用者共同使用同一个对象

  1. 假如 一个类成员 是指针,这个普通类 可以被值拷贝。 类表值语义,被多次被copy和访问和销毁。一个类成员 是指针是浅拷贝,避免更大开销 可以使用shared_ptr

  2. 多线程多读少写 读写一致性

    利用shared_ptr和互斥锁来模拟读写锁

shared_ptr 不使用条件(需要改写):双向链表

  1. 用weak_ptr解决shared_ptr的环形引用问题(避免内存泄露,无法调用析构函数)

unique_ptr 不使用条件(需要改写):容器里存放指针。指针reszie 大小

  • unique_ptr 剩下全部场景都可以用代替吗? unique_ptr默认不能copy,如果一次使用 禁止拷class
  • 虽然move实现拷贝,unique_ptr原来指针为null,有core的风险。 解决办法:实现智能指针的深度拷贝。

开始

C++11 中推出了三种智能指针,unique_ptr、shared_ptr 和 weak_ptr,同时也将 auto_ptr 置为废弃 (deprecated)。

但是在实际的使用过程中,很多人都会有这样的问题:

  • 不知道三种智能指针的具体使用场景
  • 无脑只使用 shared_ptr
  • 认为应该禁用 raw pointer(裸指针,即 Widget * 这种形式),全部使用智能指针

对象所有权

在编程语言中,对堆对象的内存管理是一个麻烦又复杂的问题。一不小心就会带来问题(堆上数据通过指针来访问。)

C++里多个变量指向同一块内存导致重复释放。本文简单探讨一下关于对象所有权的问题

  • 首先需要理清楚的概念就是对象所有权的概念

明白了对象所有权,我们才可以正确管理好对象生命周期和内存问题。

对象的所有权意味着当我们分配一个对象的时候,谁持有这个对象的所有权

Guru Questions 1(大师级问题)

既然智指针,能避免内存泄漏问题,

能代替all场景的原始指针吗?,

为什么发明三个 而不是一个,来一统天下。

  • unique_ptr 代替全部原始指针吗?

答: 不是的,如果使用不当会造成 core 或者 不执行析构函数。

在类的成员,或者函数参数传递。

boost\smart_ptr\weak_ptr.hpp

如果 weak_ptr 指向某一 shared_ptr 指针拥有的堆内存, 则 weak_ptr 也指向该块存储空间(可以访问,但无所有权)

weak_ptr:

element_type * px;            // contained pointer
boost::detail::weak_count pn; // reference counter
    
weak_count & operator= (shared_count const & r) BOOST_SP_NOEXCEPT
    {
        sp_counted_base * tmp = r.pi_;

        if( tmp != pi_ )
        {
            if(tmp != 0) tmp->weak_add_ref();
            if(pi_ != 0) pi_->weak_release();
            pi_ = tmp;
        }

        return *this;
    }

如果 weak_ptr 指向某一 shared_ptr 指针拥有的堆内存, 则 weak_ptr 也指向该块存储空间(可以访问,但无所有权)

#include <iostream>
#include <memory>
using namespace std;

int main()
{
    std::shared_ptr<int> sp1(new int(10));
    std::shared_ptr<int> sp2(sp1);
    std::weak_ptr<int> wp(sp2);
    //输出和 wp 同指向的 shared_ptr 类型指针的数量
    cout << wp.use_count() << endl;
    //释放 sp2
    sp2.reset();
    cout << wp.use_count() << endl;
    //借助 lock() 函数,返回一个和 wp 同指向的 shared_ptr 类型指针,获取其存储的数据
    cout << *(wp.lock()) << endl;
    return 0;
}

程序执行结果为: 2 1 10

shared_ptr.hpp


    element_type * px;                 // contained pointer
    boost::detail::shared_count pn;    // reference counter

敲黑板:shared_ptr 放到一个类中,这个类值传递,shared_ptr值义 ,不考虑内部的copy问题。 但是但是双向链表成员 不能用shared_ptr表示

  • 写代码验证你结果: shared_ptr 转变成 weak_ptr 不增加 shared_ptr引用计数。不回造成死循环。
#include<iostream>
#include<memory>
using namespace std;

struct Node
{
    int _value;

    //shared_ptr<Node> _next;
    //shared_ptr<Node> _prev;

    weak_ptr<Node> _next;
    weak_ptr<Node> _prev;

    ~Node()
    {
        cout << " ~Node()  "<<_value << endl;
    }
    Node( int input)
    {
        _value = input;
    }
};



int main()
{
    shared_ptr<Node> sp1(new Node(1));
    shared_ptr<Node> sp2(new Node(2));

    sp1->_next = sp2; //为什么:shared_ptr 转变成 weak_ptr 不增加 shared_ptr引用计数呢?
    sp2->_prev = sp1; //为什么:shared_ptr 转变成 weak_ptr 不增加 shared_ptr引用计数呢?

    cout<<sp1.use_count()<<endl; //引用计数-->1 不是 2

    cout << sp2.use_count() << endl; //引用计数-->1 不是 2

    return 0;
}
//https://blog.csdn.net/qq_36430106/article/details/89441856

敲黑板:

对象的延迟销毁。 陈硕在《Linux 多线程服务器端编程》中提到,当一个对象的析构非常耗时, 甚至影响到了关键线程的速度。 可以使用 BlockingQueue<std::shared_ptr > 将对象转移到另外一个线程中释放, 从而解放关键线程。

敲黑板:

  1. 利用shared_ptr和互斥锁来模拟读写锁
  2. 用weak_ptr解决shared_ptr的环形引用问题

unique_ptr

遇到的坑

//用了unique_ptr为什么会core, 这是unique_ptrbug吗?

void TestAutoPtr5() 
{
    std::cout << "TestAutoPtr5 Test" << std::endl;
    std::vector<std::unique_ptr<SimpleTest>> vc;
    vc.push_back(std::unique_ptr<SimpleTest>(new SimpleTest(5)));
    vc.push_back(std::unique_ptr<SimpleTest>(new SimpleTest(6)));
    vc.push_back(std::unique_ptr<SimpleTest>(new SimpleTest(7)));

    // 1-----
    for (std::vector<std::unique_ptr<SimpleTest>>::iterator iter = vc.begin();
         iter != vc.end(); iter++)
    {
        (*iter)->DoSomething();
    }

    // 2-----
    vc.resize(5);
    //可看出智能指针尽量不要指向vector容器类型,因为当vector扩容时,智能指针便不再生效,引起程序的崩溃或未定义的行为。
    for (std::vector<std::unique_ptr<SimpleTest>>::iterator iter = vc.begin();
         iter != vc.end(); iter++)
    {
        (*iter)->DoSomething();
    }
}

公众号:offer多多

2021/07/31  阅读:164  主题:橙心

作者介绍

公众号:offer多多