C++中的智能指针

C++中的智能指针

C++ 中的四个智能指针分别是

1.shared_ptr

2.unique_ptr

3.weak_ptr

4.auto_ptr(已经被c++11弃用)(在c++11的环境中会爆警告warning: 'auto_ptr' is deprecated)

智能指针的作用

智能指针的作用是管理一个指针。

因为存在以下这种情况:申请的空间在函数结束时忘记释放,造成内存泄漏。

使用智能指针可以很大程度上的避免这个问题,因为智能指针就是一个,当超出了类的作用域是,类会自动调用析构函数析构函数会自动释放资源。所以智能指针的作用原理就是在函数结束时自动释放内存空间,不需要手动释放内存空间。

所以智能指针的设计思想简单的来说就是:将基本类型指针封装成类(模板类)对象指针,并且在析构函数里编写delete语句删除指针所指向的内存空间

智能指针的使用

智能指针所在的头文件:#include<memory>

  • 智能指针都有一个explicit构造函数(显式构造函数)
templet<class T>
    class auto_ptr{
		explicit auto_ptr(X* p=0);
    }

所以智能指针的调用都只能显示调用

eg:

#include<bits/stdc++.h>
using namespace std;
int main(){
    shared_ptr<double> pd;
    double *ptr_reg=new double;
    pd=shared_ptr<double>(ptr_reg);
    shared_ptr<double> pshared(ptr_reg);
}

经常容易犯错的地方:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int a=5;
    shared_ptr<int> pt(&a);
}

这里我们定义的a用的是栈内存,在pt过期时,程序将把delete运算符用于非堆内存,这是错误的

  • 对于两个指针指向同一个对象的问题,有如下解决办法

    对于如下代码:

    #include<bits/stdc++.h>
    using namespace std;
    int main(){
        auto_ptr<string> ps(new string ("first blood!"));
        auto_ptr<string> tmp;
        tmp=ps;
    }
    

    有两个指针指向“first blood”这一个string对象,那么程序会试图删除这个string对象两次,众所周知,指向一个已删除的对象的指针是野指针,这样会造成内存泄漏

    为了避免野指针的出现,有如下解决方法

    1. 定义赋值运算符,使之执行深复制

      什么是深复制:它除了会将原有对象的所有成员变量拷贝给新对象,还会为新对象再分配一块内存,并将原有对象所持有的内存也拷贝过来。这样做的结果是,原有对象和新对象所持有的动态内存是相互独立的,更改一个对象的数据不会影响另外一个对象,

      这样执行的后果是浪费内存,智能指针未采用这个方案

    2. 建立所有权概念(auto_ptr、unique_ptr)

      对于特定的对象,只有一个智能指针可以拥有,这样只有拥有特定对象的智能指针可以删除该对象。赋值操作等价于转让所有权。

    3. 引用计数法(shared_ptr)

      跟踪特定对象的智能指针数,在赋值时,计数+1,过期时,计数-1,只有当引用计数减到0时才调用delete

  • 为何弃用auto_ptr

    安全问题,使用auto_ptr不当容易造成内存崩溃

    eg:

    #include<bits/stdc++.h>
    using namespace std;
    int main() {
        auto_ptr<string>  str[5] = {
            auto_ptr<string>(new string("str1")),
            auto_ptr<string>(new string("str2")),
            auto_ptr<string>(new string("str3")),
            auto_ptr<string>(new string("str4")),
            auto_ptr<string>(new string("str5")),
        };
        auto_ptr<string> ps;
        ps = str[2];
        for(int i = 0; i < 5; i++) {
            cout << *str[i] << endl;
        }
    }
    

    ​ 运行改代码时,输出到str2时就程序停止运行了,这里是因为ps智能指针拿走了str[2]的所有权,在输出时访问空指针就会使得程序崩溃

    ​ 而将auto_ptr换为shared_ptr或unique_ptr时,程序却不会崩溃

    ​ 使用unique_ptr时,程序会报错:

    note: declared here unique_ptr& operator=(const unique_ptr&) = delete;

    所以auto_ptr就被弃用了23333

  • unique_ptr的优势

    首先同样是所有权模式的智能指针,unique会在指针所有权被剥夺后再访问报错

    其次,会及时销毁临时的指针

    eg:

    #include<bits/stdc++.h>
    using namespace std;
    unique_ptr<string> get(const char *s) {
        unique_ptr<string> tmp(new string(s));
        return tmp;
    }
    int main() {
        unique_ptr<string> ps;
        ps = get("23333");
        cout<<*ps<<endl;
    }
    

    对于get函数返回的一个临时指针的所有权被ps指针拿到后,临时指针就被销毁了,这个临时指针不会访问无效数据,即unique_ptr(new Temp(val))可以作为一个临时右值出现

    如果想将一个指针安全的赋值给另一个指针,可以使用c++标准库当中的move函数

    eg:

    #include<bits/stdc++.h>
    using namespace std;
    unique_ptr<string> get(const char *s) {
        unique_ptr<string> tmp(new string(s));
        return tmp;
    }
    int main() {
        unique_ptr<string> ps1, ps2;
        ps1 = get("str1");
        ps2 = move(ps1);
        ps1 = get("str2");
        cout << *ps2 << " " << *ps1 << endl;
    }
    

智能指针的选择

(1)如果程序要使用多个指向同一个对象的指针,应选择shared_ptr。这样的情况包括:

  • 有一个指针数组,并使用一些辅助指针来标示特定的元素,如最大的元素和最小的元素;
  • 两个对象包含都指向第三个对象的指针;
  • STL容器包含指针。很多STL算法都支持复制和赋值操作,这些操作可用于shared_ptr,但不能用于unique_ptr(编译器发出warning)和auto_ptr(行为不确定)。如果你的编译器没有提供shared_ptr,可使用Boost库提供的shared_ptr。

(2)如果程序不需要多个指向同一个对象的指针,则可使用unique_ptr。

如果函数使用new分配内存,并返还指向该内存的指针,将其返回类型声明为unique_ptr是不错的选择。

这样,所有权转让给接受返回值的unique_ptr,而该智能指针将负责调用delete。可将unique_ptr存储到STL容器在那个,只要不调用将一个unique_ptr复制或赋给另一个算法(如sort())。

在unique_ptr为右值时,可将其赋给shared_ptr,这与将一个unique_ptr赋给一个需要满足的条件相同。

模板shared_ptr包含一个显式构造函数,可用于将右值unique_ptr转换为shared_ptr。shared_ptr将接管原来归unique_ptr所有的对象。

eg:

#include<bits/stdc++.h>
using namespace std;
unique_ptr<int> get_int(int n) {
    return unique_ptr<int>(new int(n));
}
int main() {
    srand ( (unsigned int) time (NULL) );
    unique_ptr<int> up1(get_int(rand() % 100 + 1));
    shared_ptr<int> sp1(get_int(rand() % 10 + 1));
    cout<<*up1<<" "<<*sp1<<endl;
}
原文地址:https://www.cnblogs.com/buerdepepeqi/p/12293499.html