C++智能指针

发布时间:2024年01月12日

智能指针类型

最初,c++98首先引入了auto_ptr, 在接下来的c++11中又引入了unique_ptr, shared_ptr以及weak_ptr。

引入智能指针的目的

由于c++赋予了程序员直接操作内存的权限,使得c++程序的执行效率可以非常高。但是,直接操作内存也引入了很多潜在的问题,比如说内存泄漏或者野指针的问题。说到这两种问题,就有必要提一下c++的内存分配机制。c++内存大致可分为全局区(静态区),堆区和栈区,全局区中的变量在程序执行过程中一直存在,栈区的变量死亡后OS便会进行内存回收,堆区用来存储动态分配的内存,动态对象的内存由程序来控制,也就是说我们需要在代码中显式的销毁内存。

#include <iostream>

int data = 10; // 全局区

int main()
{
    int a = 10; // 栈区
    int* pre = new int(10); //堆区

    return 0;
}

coder在代码中分配一块内存后,需要显式的释放,若过早释放会带来野指针的问题,若没有释放则会造成内存泄漏的问题。为了解决手动释放动态对象存在的潜在问题,智能指针被提出,它可以帮助coder在合适的位置释放内存,极大降低了程序中的内存问题。

auto_ptr

auto_ptr为比较早提出的智能指针,本身这个类型有些缺陷,最好不要使用(大部分公司应该是强制不可使用),可以使用unique_ptr来代替auto_ptr。

auto_ptr<int> a1(new int(10));
auto_ptr<int> a2(new int(20));
a1 = a2;
printf("%d\n", *a2); // 错误,上一步操作将a2对资源的管理权给到了a1,此时再对a2访问会造成crush

unique_ptr

相比auto_ptr,unique_ptr不在允许拷贝和赋值,防止出现上面提到的auto_ptr的问题(通过delete),下面代码大致写了一下unique_ptr的主要功能。

template <typename T>
class unique_ptr
{
public:
    unique_ptr(T* p = nullptr) // 由此可见unique_ptr缺省值是nullptr,但是一个裸指针未初始化的        
        : ptr_(ptr)            // 情况下不是nullptr,而是一个野指针!!!
    {}
    ~unique_ptr()
    {
        if (ptr_ != nullptr) 
        {
            delete ptr_;
            ptr_ = nullptr; // 注意,如果只是delete指针的话并不会将指针置为nullptr
        }
    }

    unique_ptr(unique_ptr<T>& up) = delete; // 禁止拷贝
    unique_ptr& operator=(unique_ptr<T>& up) = delete; // 禁止赋值

    // 以下两个函数在所有智能指针的类中都存在,重载运算符来使得智能指针可以和普通指针有相同的操作    
       逻辑
    T& operator*()
    {
        return *ptr_;
    }
    T* operator->()
    {
        return ptr_;
    }

private:
    T* ptr_;
}

shared_ptr

unique_ptr只允许资源被唯一管理,但有时候该指针可能需要被多个对象管理,此时引入了shared_ptr,相比unqiue_ptr,shared_ptr中多了一个计数器。每当shared_ptr被一个新的对象所管理,则计数器+1,当计数器个数为0时,则释放shared_ptr所管理的资源,模拟实现如下(和unqiue_ptr共有的内容不在展示)

template <typename T>
class shared_ptr
{
public:
    shared_ptr(T* ptr = nullptr):
        : ptr_(ptr)
        , count_(new int(1))

    {}
    ~shared_ptr()
    {
        if (--(*count_) == 0) { // 根据计数器来判断是否需要释放内存
            if (ptr_ != nullptr) 
            {
                delete ptr_;
                ptr_ = nullptr;
            }
            delete count_;
            count_ = nullptr;
        }
    }

    shared_ptr(shared_ptr<T>& sp)
        : ptr(sp.ptr_)
        , count_(sp.count_)
    {
        ++(*count_); // 计数器增加
    }

    shared_ptr<T>& operator=(shared_ptr<T>& sp)
    {
        if (ptr_ != sp.ptr_) 
        {
            if (--(*count_) == 0) // 判断是否需要释放原来所管理的资源
            {
                delete count_;
                count_ = nullptr;
            }
            ptr_ = sp.ptr_;
            count_ = sp.count_;
            ++(*count_);
        }
    }

    int use_count()
    {
        return *count_;
    }

private:
    T* ptr_;
    int* count_;
}

weak_ptr

weak_ptr是为了解决shared_ptr循环引用带来的问题,不是特别常用。本质上就是保存一个shared_ptr的裸指针,实现如下:

template <typename T>
class weak_ptr
{
public:
    weak_ptr(const shared_ptr<T>& sp)
        : ptr_(sp.get()) // 获取裸指针
    {}
    weak_ptr& operator=(cosnt shared_ptr<T>& sp)
    {
        ptr = sp.get();
        return *this;
    }

private:
    T* ptr_;
}

注意事项

1. 不要用智能指针的裸指针来新建智能指针

2. 使用智能指针的裸指针时要想清楚作用域

3. 有时候可以使用裸指针来提升代码执行效率,比如说明确知道在一个范围内智能指针不会释放,则在这个范围内可以使用该智能指针的裸指针进行传递。

文章来源:https://blog.csdn.net/qq_35644837/article/details/135537349
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。