智能指针

目录

C++里面的3个智能指针:  

为什么要使用智能指针:

shared_ptr是怎么实现的?什么时候改变引用计数?

shared_ptr循环引用问题:

所以改用weak_ptr(好好看看showfriend这个函数,看怎么用weak_ptr)

shared_ptr基本使用代码:

shared_ptr基本实现代码:


参考:

https://www.cnblogs.com/xiehongfeng100/p/4645555.html

https://zhuanlan.zhihu.com/p/73807983           

https://blog.csdn.net/qq_34992845/article/details/69218843 (这个更详细的分析了下循环引用的源码,但我觉得例子不行,不如我的 嘻嘻嘻)

C++里面的3个智能指针:  

  1. shared_ptr  (允许多个指针指向同一个对象)
  2. weak_ptr   (弱引用,,可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造,拷贝,析构等一系列动作都不会引起引用计数的变化)
  3. unique_ptr  (独占所指的对象,具体方法是禁止拷贝构造和拷贝赋值操作)

头文件  <memory>

weak_ptr补充:weak_ptr被设计为与shared_ptr共同工作,可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。同样,在weak_ptr析构时也不会导致引用计数的减少,它只是一个静静地观察者。weak_ptr没有重载operator*和->,这是特意的,因为它不共享指针,不能操作资源,这是它弱的原因。如要操作资源,则必须使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象,从而操作资源。

 

为什么要使用智能指针:

智能指针是C++  RAII(Resource Acquisition Is Initialization)(资源获取即初始化)思想的一种体现。用类来帮助我们管理资源,并在类的析构函数中去释放资源,这样做的好处就是不需要显式地释放资源,减少了资源泄漏的风险。

智能指针主要用于管理在堆上分配的内存,它将普通的指针封装为一个栈对象。当栈对象的生存周期结束后,会在析构函数中释放掉申请的内存,从而防止内存泄漏。C++ 11中最常用的智能指针类型为shared_ptr,它允许多个指针指向同一个对象,用一个分配在堆上面的引用计数来记录当前内存资源被多少个智能指针引用。当新增一个时引用计数加1,析构的时候引用计数减一。并且引用计数为0的时候,析构函数会自动释放引用的内存资源。对shared_ptr进行初始化时不能将一个普通指针直接赋值给智能指针,因为智能指针的实现里面没有实现这个操作。可以通过make_shared函数或者通过构造函数传入普通指针。并可以通过get函数获得普通指针。

智能指针也可以管理其他类型的资源,不过这时候需要我们提供相应的释放的方法(默认会调用delete)。

shared_ptr是怎么实现的?什么时候改变引用计数?

  1. 构造函数中计数初始化为1;
  2. 拷贝构造函数中计数值加1;
  3. 赋值运算符中,左边的对象引用计数减一,右边的对象引用计数加一;(先加1,后减1)
  4. 析构函数中引用计数减一;
  5. 在赋值运算符和析构函数中,如果减一后为0,则调用delete释放对象。
  6. share_prt与weak_ptr的区别?

shared_ptr循环引用问题:

简单讲就是两份资源A和B互相拥有对方的共享所有权,并且我们在A和B的内部使用shared_ptr来保存这个所有权。

比如下面这个例子:

小王和小李都是人,我们用shared_ptr来管理这两个人,这很ok!但是每个人都会有自己的best_friend,所以我们在person这个类里面定义了一个数据成员 best_friend,并且为了能更好管理人这个资源,我们用shared_ptr,然后就gg了。

考虑下面这种情况,小王和小李他们互为好朋友,现在,小王和小李这两个资源的引用计数就都是2了,这也直接造成了,当离开作用域的时候,他们的引用计数只是都变为1,但是并没有得到释放

struct person{
    
    person(string _name):myname(_name){};
    
    string myname;
   
    shared_ptr<person> best_friend;
        

}

int main(){

    shared_ptr<person> p1(new person("小王"));  // 小王引用计数:1
    shared_ptr<person> p2(new person("小李"));  // 小李引用计数:1
    p1->best_friend=p2;  // 现在小李引用计数:2 小王:1
    p2->best_friend=p1;  // 小李:2 小王:2

    //析构,gg
} 

所以改用weak_ptr(好好看看showfriend这个函数,看怎么用weak_ptr)

struct person{
    
    person(string _name):myname(_name){};
    
    string myname;
   
    weak_ptr<person> best_friend;
    
    // 好好看看这个函数
    void showfriend(){
        if(!best_friend.expired())
            cout<<best_friend.lock()->myname<<endl;
    }    

}

 

shared_ptr基本使用代码:

//shared_ptr 使用方式

#include <iostream>
#include <string>

#include <memory> // 必要头文件
using namespace std;

int main(void)
{
    shared_ptr<string> str1=make_shared<int>(string("abc"))
    shared_ptr<string> str2(new string("abc"));
    
    //自定义删除操作,传递一个函数指针
    shared_ptr<string> pJutta(new string("jutta"),
            // deleter (a lambda function) 
            [](string *p)
            { 
                cout << "delete " << *p << endl;
                delete p;
            }
        );



    return 0;
}

shared_ptr基本实现代码:

其实shared_ptr内部实现,引用计数是先指向一块控制台,控制台里面有引用计数,弱引用等等,以后有时间看effective moden c++ 条款 19吧

#include <iostream>
using namespace std;

template<class T>
class SmartPtr
{
public:
    SmartPtr(T *p);
    ~SmartPtr();
    SmartPtr(const SmartPtr &rhs);                // 浅拷贝
    SmartPtr& operator=(const SmartPtr &rhs);    // 浅拷贝
    
    T& operator*();
    T* operator->();
    
private:
    T *ptr;
    // 将use_count声明成指针是为了方便对其的递增或递减操作
    int *use_count;
};

template<class T>
SmartPtr::SmartPtr(T *p) : ptr(p)
{
    use_count=new int(1);

}

template<class T>
SmartPtr::~SmartPtr()
{
    // 只在最后一个对象引用ptr时才释放内存
    if (--(*use_count) == 0)
    {
        delete ptr;
        delete use_count;
        ptr = nullptr;
        use_count = nullptr;
    }
}

template<class T>
SmartPtr::SmartPtr(const SmartPtr &rhs)
{
    ptr = rhs.ptr;
    use_count = rhs.use_count;
    ++(*use_count);

}

// 重载等号函数不同于复制构造函数,即等号左边的对象可能已经指向某块内存。
// 这样,我们就得先判断左边对象指向的内存已经被引用的次数。如果次数为1,
// 表明我们可以释放这块内存;反之则不释放,由其他对象来释放。
template<class T>
SmartPtr& SmartPtr::operator=(const SmartPtr &rhs)
{
    // 《C++ primer》:“这个赋值操作符在减少左操作数的使用计数之前使rhs的使用计数加1,
    // 从而防止自身赋值”而导致的提早释放内存
    ++(*rhs.use_count);

    // 将左操作数对象的使用计数减1,若该对象的使用计数减至0,则删除该对象
    if (--(*use_count) == 0)
    {
        delete ptr;
        delete use_count;
        cout << "Left side object is deleted!" << endl;
    }

    ptr = rhs.ptr;
    use_count = rhs.use_count;
    
    cout << "Assignment operator overloaded is called!" << endl;
    return *this;
}


template<class T>
T& SmartPtr::operator*(){
    return *ptr;
}


template<class T>
T* SmartPtr::operator->(){
    return ptr;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值