c++11智能指针

前言

c++中的指针无疑是c++的精髓,极大地提升了编程的灵活性,但各种奇怪的泄漏问题也接踵而至。


内存泄漏是指程序中已经动态分配的堆内存未被释放或者无法释放,通常是由于通过new/new[]分配的对象没有匹配地调用delete/delete[],造成系统内存的浪费,严重时甚至会导致系统崩溃。
为了解决这些问题,c++引入了auto_ptr,但由于auto_ptr引入了更多的问题,通常被弃用。所以c++11中引入了三种新的智能指针:shared_ptr、weak_ptr和unique_ptr。智能指针基于RAII的思想,将指针封装为对象,在对象初始化时申请内存,对象析构时释放内存。这样就使得智能指针在离开其作用域时,自动调用析构函数释放内存,保证了内存安全。

unique_ptr

unique_ptr是不可共享的智能指针,这种智能指针通过将拷贝构造函数和赋值运算符声明为私有的,避免了unique_ptr在外部被引用,从而保证其唯一性。下面代码提供了一种unique_ptr的实现。

template <typename T>
class my_unique_ptr {
public:
    my_unique_ptr() : t_(nullptr){}

    my_unique_ptr(T* t) : t_(t){}

    ~my_unique_ptr() {
        delete t_;
    }

    my_unique_ptr& operator*() {
        return *this;
    }

    T* operator->() {
        return t_;
    }

    T* get() const {
        return t_;
    }


private:
    T* t_;

    my_unique_ptr& operator=(const my_unique_ptr& rhs) {
    	t_ = rhs.t_;
        return *this;
    }

    my_unique_ptr(const SharedPtr& rhs) {
        t_ = rhs.t_;
    }

};

shared_ptr和weak_ptr

shared_ptr是可共享的智能指针,为解决其释放问题,shared_ptr引入了引用计数,下图为下面代码对应的模型,每次对同一shared_ptr的引用,都会使得其引用计数加一。

#include <bits/stdc++.h>


int main(){
    std::shared_ptr<int> p1 = std::make_shared<int>(4);
    std::cout << p1.use_count() << std::endl; //引用计数为1


    std::shared_ptr<int> p2 = p1;
    std::cout << p1.use_count() << std::endl; //引用计数为2

    return 0;
}

智能指针原理

在释放shared_ptr的时候,先将引用计数减一,此时若引用计数为0,则释放内存,否则不释放内存。
有时我们会遇到如下框架的代码:

#include <bits/stdc++.h>

class B;
class A{
public:
    std::shared_ptr<B> b;
};

class B{
public:
    std::shared_ptr<A> a;
};


int main(){
    
    std::shared_ptr<A> p1 = std::make_shared<A>();
    std::shared_ptr<B> p2 = std::make_shared<B>();
    p1->b = p2;
    p2->a = p1; 
    std::cout << p1.use_count() << std::endl; //引用计数为2
    std::cout << p2.use_count() << std::endl; //引用计数为2
    return 0;
}

在打印结果中,p1和p2的引用计数均为2,在离开作用域时,引用计数均减一变为1,但都没有释放内存,造成了内存泄漏。这种问题我们称之为shared_ptr的循环引用,为了解决循环引用问题,c++11引入了weak_ptr,weak_ptr不参与引用计数,如下面代码。

class B;
class A{
public:
    std::weak_ptr<B> b;
};

class B{
public:
    std::weak_ptr<A> a;
};


int main(){
    
    std::shared_ptr<A> p1 = std::make_shared<A>();
    std::shared_ptr<B> p2 = std::make_shared<B>();
    p1->b = p2;
    p2->a = p1; 
    std::cout << p1.use_count() << std::endl; //引用计数为1
    std::cout << p2.use_count() << std::endl; //引用计数为1
    return 0;
}

这里也提供一种shared_ptr的简单实现。

template <typename T>
class my_shared_ptr {
public:
    my_shared_ptr() : t_(nullptr), count_(nullptr) {
    }

    my_shared_ptr(T* t) : t_(t), count_(nullptr) {
        if(t) { count_ = new int(1); }
    }

    my_shared_ptr(const SharedPtr& rhs) {
        t_ = rhs.t_;
        count_ = rhs.count_;
        if(count_) {
            (*count_)++;
        }
    }

    ~my_shared_ptr() {
        reset();
    }

    my_shared_ptr& operator=(const my_shared_ptr& rhs) {
        if(this == &rhs) {
            return *this;
        }

        reset();
        t_ = rhs.t_;
        count_ = rhs.count_;
        if(count_) {
            (*count_)++;
        }
        return *this;
    }

    my_shared_ptr& operator*() {
        return *this;
    }

    T* operator->() {
        return t_;
    }

    T* get() const {
        return t_;
    }

    int use_count() const {
        return count_ ? *count_ : 0;
    }

    void reset() {
        if(count_) {
            (*count_)--;
            if(*count_ == 0) {
                delete t_;
                delete count_;
            }
        }
    }

private:
    T* t_;
    int* count_;
};

总结

不管是unique_ptr和shared_ptr,个人理解都是在处理指针所有权的问题,unique_ptr避免指针的浅拷贝,shared_ptr通过引用计数共享所有权,如果想移交所有权,可以使用std::move移动语义,这里我们下篇文章继续讨论。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值