C++智能指针模板应用详细介绍

目录
  • 智能指针模板类
  • 使用智能指针
  • 关于智能指针的注意事项
  • unique_ptr优于auto_ptr
  • 选择智能指针
  • weak_ptr

智能指针模板类

void remodel(std::string & str)
{
    std::string *ps =new std::string(str);
    ....
    if(oh_no)
        throw exception();
    ...
    delete ps;
    return;
}

如果上面这段函数出现异常,那么就会发生内存泄漏。传统指针在执行动态内存分配时具有缺陷,很容易导致内存泄漏。如果有一个指针,指针被释放的同时,它指向的内存也能被释放,那就完美了。这种指针就是智能指针。

我们只介绍3个智能指针模板类:auto_ptrunique_ptrshared_ptr,顺便会提一下weak_ptr。其中auto_ptr已经被抛弃了,它是一个过时的产物,我们介绍它只为抛砖引玉。

使用智能指针

这些智能指针模板定义了类似指针的对象,我们把new获得的地址赋给这种对象,当智能指针过期时,它的析构函数会自动释放动态内存。

必须包含头文件memory,这个文件包含模板定义。我们使用模板实例化来创建所需指针.

类模板大概是:

template<class X>
class auto_ptr
{
public:
    explicit auto_ptr(X* p) noexcept;
    ....
}

所以我们实例化:

auto_ptr<double> pd(new double);或者auto_ptr<string> ps(new string);

对于其他两种智能指针也是一样的构造语法。

//智能指针1.cpp
#include<iostream>
#include<string>
#include<memory>
class Report
{
private:
    std::string str;
public:
    Report(const std::string &s):str(s){std::cout<<"Object created!\n";}
    ~Report(){std::cout<<"Object deleted";}
    void comment() const{std::cout<<str<<"\n";}
};
int main()
{
    {
        std::auto_ptr<Report> ps(new Report("using auto_ptr"));
        ps->comment();
    }
    {
        std::unique_ptr<Report> ps(new Report("using unique_ptr"));
        ps->comment();
    }
    {
        std::shared_ptr<Report> ps(new Report("using shared_ptr"));
        ps->comment();
    }
}

Object created!
using auto_ptr
Object deletedObject created!
using unique_ptr
Object deletedObject created!
using shared_ptr
Object deleted

注意,所有智能指针类都有一个explicit构造函数,该构造函数将指针作为参数。所以它不会将指针转换成智能指针对象。

shared_ptr<double> pd;
double *p_reg=new double;
pd=p_reg;//不允许因为构造函数是`explicit`修饰的,所以不能隐式类型转换
pd=shared_ptr<double>(p_reg);//允许,使用赋值运算符
shared_ptr<double> pshared=p_reg;//不允许,因为不能隐式类型转换
shared_ptr<double> pshared(p_reg);//允许调用构造函数。

智能指针和传统指针有很多类似的语法:例如可以使用*ps来解除引用,也可以使用ps->来访问结构成员。

但是最重要的不同是:我们只能用能进行delete或者delete[]的指针来构造智能指针。

也就是说:

int a=5;
int *p=&a;
shared_ptr<int> ps(p);

上面这段代码就是错误的,因为p无法使用delete.

#include<memory>
#include<iostream>
int main()
{
    using namespace std;
    /*{
        auto_ptr<int[]> ps(new int[2]{1,2});
        cout<<ps[0]<<endl;
        cout<<ps[1]<<endl;
    }*/
    {
        unique_ptr<int[]> ps(new int[2]{1,2});
        cout<<ps[0]<<endl;
        cout<<ps[1]<<endl;
    }
    {
        shared_ptr<int []> ps(new int[2]{1,2});
        cout<<ps[0]<<endl;
        cout<<ps[1]<<endl;
    }
}

上面代码告诉我们,我们可以使用<int []>这样实例化模板,这是为了模拟动态数组。

关于智能指针的注意事项

auto_ptr<string> ps(new string("I reigned lonely as a cloud."));
auto_ptr<string> pd;
pd=ps;

上面这段代码不会报错,但是你可能会问:pd和ps都是智能指针,如果我们把ps赋给pd;那么就说明这两个指针指向同一个string对象,那么当这两个指针消失时,会对同一个string对象释放两次?

我们看看我们如何避免这种问题:

  • 进行深复制
  • 建立所有权概念,对于特定的对象,只能有一个智能指针拥有它,这样就只有拥有它的智能指针的析构函数才会释放内存。然后赋值运算会转让所有权。auto_ptrunique_ptr都是使用这种策略,但是unique_ptr更严格。
  • 使用引用计数,每个对象都会记录有多少个智能指针指向它,然后赋值运算时,计数加1,指针过期时计数减1。当最后一个指针过期时,才会调用delete,这是shared_ptr的策略。

实际上,上述这些策略也适用于复制构造函数。

实际上,unique_ptr就是"唯一指针",指针和被指向的对象一一对应,而shared_ptr就是"分享指针",它允许多个指针指向同一个对象。所以说,shared_ptr的用法更像C风格指针。

我们看上面的代码,pd=ps后,由于string对象的所有权交给了pd,所以*ps就无法使用了。

//智能指针3.cpp
#include<iostream>
#include<string>
#include<memory>
int main()
{
    using namespace std;
    auto_ptr<string> films[5]=
    {
        auto_ptr<string>(new string("1")),
        auto_ptr<string>(new string("2")),
        auto_ptr<string>(new string("3")),
        auto_ptr<string>(new string("4")),
        auto_ptr<string>(new string("5"))
    };
    auto_ptr<string> p;
    p=films[2];
    for(int i=0;i<5;i++)
    {
        cout<<*films[i]<<endl;
    }
    cout<<*p<<endl;
}

上面这段代码会出错,因为p=films[2];使得,films[2]的所有权转让给p了,所以cout<<*film[2]就会出错。但是如果使用shared_ptr代替auto_ptr就可以正常运行了。如果使用unique_ptr呢?程序会在编译阶段报错,而不是在运行阶段报错,所以说unique_ptr更加严格。

unique_ptr优于auto_ptr

首先就是上面谈过的,unique_ptr的所有权概念比auto_ptr要严格,所以unique_ptr更加安全。

unique_ptr<string> ps(new string("I reigned lonely as a cloud."));
unique_ptr<string> pd;
pd=ps;

上述代码会在编译阶段报错,因为出现了危险的悬挂指针ps(即野指针,指针指向被删除的内存,如果使用野指针修改内存是会造成严重后果)。

但是有时候将一个智能指针赋给另一个并不会留下悬挂指针:

unique_ptr<string> demo(const char*s)
{
    unique_ptr<string> temp(new string(s));
    return temp;
}
...
unique_ptr<string>ps;
ps= demo("something");
...

demo()函数返回一个临时变量temp,然后临时变量temp被赋给ps,那么temp就变成悬挂指针了,但是我们知道ps=demo("something")一旦运行结束,demo()里的所有局部变量都会消失包括temp。所以即使temp是野指针,我们也不会使用它。神奇的是,编译器也允许上面这种赋值。

总之,程序试图将一个unique_ptr赋给另一个时,如果源unique_ptr是个临时右值,编译器允许这么做;如果源unique_ptr会存在一段世界,编译器禁止这么做。

unique_ptr<string> pu1;
pu1=unique_ptr<string>(new string("yo!"));

上面这段代码也是允许的,因为unique_ptr<string>(new string("yo!"))是一个临时右值(右值都是临时的,右值只在当前语句有效,语句结束后右值就会消失)

unique_ptr<string> ps(new string("I reigned lonely as a cloud."));
unique_ptr<string> pd;
pd=std::move(ps);

上面代码是正确的,如果你想要进行将unique_ptr左值,赋给unique_ptr左值,那么你必须使用move()函数,这个函数会将左值转换成右值。

以上所说,反映了一个事实:unique_ptrauto_ptr安全。其实unique_ptr还有一个优点:auto_ptr的析构函数只能使用delete,而unique_ptr的析构函数可以使用delete[]delete

选择智能指针

首先明确一个事实:shared_ptr更方便;unique_ptr更安全。

如果程序需要适用多个指向同一个对象的指针,那么只能选择shared_ptr;如果不需要多个指向同一个对象的指针,那么两种指针都可以使用。总之,嫌麻烦的话就全部用shared_ptr.

#include<memory>
#include<iostream>
#include<vector>
#include<cstdlib>
#include<algorithm>
std::unique_ptr<int> make_int(int n)
{
    return std::unique_ptr<int>(new int(n));
}
void show(const std::unique_ptr<int> &pi)
{
    std::cout<<*pi<<' ';
}
int main()
{
    using std::vector;
    using std::unique_ptr;
    using std::rand;
    int size=10;
    vector<unique_ptr<int>> vp(size);
    for(int i=0;i<size;i++)
        vp[i]=make_int(rand()%1000);//#1
    vp.push_back(make_int(rand()%1000));//#2
    std::for_each(vp.begin(),vp.end(),show);//#3
}

上面这段代码是使用unique_ptr写的,#1.#2是没有问题的,因为函数返回值是临时右值,#3就要注意了.show()函数使用的是引用参数,如果换成按值传递,那就会出错,因为这会导致,使用unique_ptr左值初始化pi,这时不允许的,记得吗?在使用unique_ptr时,它的赋值运算符要求:只能用右值赋给左值。(实际上,它的复制构造函数也要求只接受右值)。

unique_ptr是右值的时候,我们可以把他赋给shared_ptr

shared_ptr包含一个显式构造函数,他会把右值unique_ptr转换成shared_ptr:

unique_ptr<int> pup(make_int(rand()%1000));//ok
shared_ptr<int> spp(pup);//不允许,构造函数不能接受`unique_ptr`的左值
shared_ptr<int> spr(make_int(rand()%1000));//ok

weak_ptr

weak_ptr正如它名字所言:一个虚弱的指针,一个不像是指的指针。weak_ptr是用来辅助shared_ptr的。

为什么说weak_ptr不像指针呢?是因为它没有重载*[]运算符。

通常,我们使用shared_ptr来初始化weak_ptr,那么这两个指针都指向是同一块动态内存。

weak_ptrshared_ptr的辅助,所以它帮忙能查看这块动态内存的信息:包括引用计数、存的信息。

#include<memory>
#include<iostream>
int main()
{
    using std::shared_ptr;
    using std::weak_ptr;
    using std::cout;
    using std::endl;
    shared_ptr<int> p1(new int(255));
    weak_ptr<int>wp(p1);
    cout<<"引用计数: "<<wp.use_count()<<endl;
    cout<<"存储信息: "<<*(wp.lock())<<endl;
    shared_ptr<int> p2=p1;
    cout<<"引用计数: "<<wp.use_count()<<endl;
    cout<<"存储信息: "<<*(wp.lock())<<endl;
}

引用计数: 1  
存储信息: 255
引用计数: 2  
存储信息: 255

weak_ptr的类方法中use_count()查看指向和当前weak_ptr指针相同的shared_ptr指针的数量,lock()函数返回一个和当前weak_ptr指针指向相同的shared_ptr指针。

到此这篇关于C++智能指针模板应用详细介绍的文章就介绍到这了,更多相关C++智能指针内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++智能指针之shared_ptr详解

    目录 共享指针的初始化方式 常用成员函数 shared_ptr内存模型 make_shared的优缺点 优点 缺点 引用计数 比较运算符 总结 共享指针的初始化方式 1.裸指针直接初始化,但不能通过隐式转换来构造 2.允许移动构造,也允许拷贝构造 3.通过make_shared构造 例: #include <iostream> #include <memory> class Frame {}; int main() { std::shared_ptr<Frame> f(

  • C++深入分析讲解智能指针

    目录 1.简介 2.unique_ptr指针(独占指针) 3.shared_ptr指针(共享所有权) 4.weak_ptr(辅助作用) 5.自实现初级版智能指针 6.总结 1.简介 程序运行时存在静态空间.栈和堆区,用堆来存储动态分配空间的对象即那些在程序运行时分配空间的对象,若该对象不再使用,我们必须显式的销毁它们,避免内存泄漏. 智能指针是一个可以像指针一样工作的对象,有unique_ptr(独占指针),shared_ptr与weak_ptr等智能指针,定义在<memory>头文件中,可以

  • C++智能指针详解

    目录 一. unique_ptr独占指针 特点 创建方式 传递方式 简单使用 隐藏危险 二. shared_ptr 计数指针 特点 传递方式 隐藏危险 三. weak_ptr 优缺点: 智能指针由原始指针的封装,优点是可以自动分配内存,不用担心内存泄漏问题. 用于解决独占/共享所有权指针的释放,传输等问题. 但是没有原始指针方便. 一. unique_ptr独占指针 特点 都是围绕独占展开 特点一: 如其名,独占.也就是说同一个内存空间同时只能有一个指针来管理. int* pi = new in

  • c++智能指针的超详细讲解

    目录 1.什么是智能指针 2.原始指针的问题 3.unique_ptr 4.shared_ptr 5.shared_ptr使用需要注意的点 5.1 不能将一个原始指针初始化多个shared_ptr 5.2.循环引用问题 6.智能指针小结 总结 1.什么是智能指针 从比较简单的层面来看,智能指针是RAII(Resource Acquisition Is Initialization,资源获取即初始化)机制对普通指针进行的一层封装.这样使得智能指针的行为动作像一个指针,本质上却是一个对象,这样可以方

  • C++智能指针shared_ptr

    目录 1.什么是shared_ptr? 2.shared_ptr支持哪些操作? 3.如何创建shared_ptr的实例? 4.什么是shared_ptr的引用计数?如何查看? 5.shared_ptr何时释放其所指向的对象? 1.什么是shared_ptr? C++11中包括shared_ptr在内的多种指针,都是模板类型,意味着使用者可以指定想要操作的类型. 创建shared_ptr的方式如下: shared_ptr<int>p1; // p1=NULL 2.shared_ptr支持哪些操作

  • C++超详细讲解智能指针

    目录 一.内存泄漏-永恒的话题 二.深度思考 三.智能指针分析 四.小结 一.内存泄漏-永恒的话题 动态申请堆空间,用完后不归还 C++ 语言中没有垃圾回收的机制 指针无法控制所指堆空间的生命周期 下面看一段内存泄漏的代码: #include <iostream> #include <string> using namespace std; class Test { int i; public: Test(int i) { this->i = i; } int value()

  • 一起聊聊C++中的智能指针

    目录 一:背景 二:关键词解析 1. auto_ptr 2. auto_ptr 多引用问题 一:背景 我们知道 C++ 是手工管理内存的分配和释放,对应的操作符就是 new/delete 和 new[] / delete[], 这给了程序员极大的自由度也给了我们极高的门槛,弄不好就得内存泄露,比如下面的代码: void test() { int* i = new int(10); *i = 10; } int main() { test(); } 这段代码因为用了 new 而忘了 delete,

  • C++智能指针之shared_ptr的具体使用

    目录 std::shared_ptr概念 shared_ptr模板类 shared_ptr的构造和析构 shared_ptr赋值 make_shared 计数线程安全? enable_shared_from_this shared_ptr使用注意事项: 总结 std::shared_ptr概念 unique_ptr因为其局限性(独享所有权),一般很少用于多线程操作.在多线程操作的时候,既可以共享资源,又可以自动释放资源,这就引入了shared_ptr. shared_ptr为了支持跨线程访问,其

  • C++智能指针模板应用详细介绍

    目录 智能指针模板类 使用智能指针 关于智能指针的注意事项 unique_ptr优于auto_ptr 选择智能指针 weak_ptr 智能指针模板类 void remodel(std::string & str) { std::string *ps =new std::string(str); .... if(oh_no) throw exception(); ... delete ps; return; } 如果上面这段函数出现异常,那么就会发生内存泄漏.传统指针在执行动态内存分配时具有缺陷,

  • C++模板超详细介绍

    目录 1.前言 2.函数模板 3.类模板 1.前言 模板是泛型编程的基础,泛型编程即以一种独立于任何特定类型的方式编写代码. 模板是创建泛型类或函数的蓝图或公式. 通常有两种形式:函数模板和类模板: 2.函数模板 函数模板针对仅参数类型不同的函数 一般形式为: template <类型形式参数表> 类型 函数名( 形式参数表) { // 函数的主体 } 函数调用实例: #include <iostream> #include <string> using namespa

  • C++ STL 四种智能指针的用法详解

    0.前言 C++ 标准模板库 STL(Standard Template Library) 一共给我们提供了四种智能指针:auto_ptr.unique_ptr.shared_ptr 和 weak_ptr,其中 auto_ptr 是 C++98 提出的,C++11 已将其摒弃,并提出了 unique_ptr 替代 auto_ptr.虽然 auto_ptr 已被摒弃,但在实际项目中仍可使用,但建议使用更加安全的 unique_ptr,后文会详细叙述.shared_ptr 和 weak_ptr 则是

  • 一文掌握C++ 智能指针全部用法

    目录 一.为什么要使用智能指针 二.auto_ptr 三.unique_ptr 四.shared_ptr 五.weak_ptr 六.智能指针的使用陷阱 七.总结 为什么要学习智能指针? 咳咳,这个问题不是问大家的,是询问我自己的! 我依稀记得刚离校出来找实习工作那会,去面试一份工作,其中有一个环节需要答题:有一道题目就是问什么是智能指针?卧槽?当时我就懵逼,智能指针我压根就没有听说过- 最后,面试的这份工作理所应当的黄了. 差不多是一年前左右吧,现在趁有闲余时间,学习一下智能指针,丰富一下自己!

  • c++动态内存管理与智能指针的相关知识点

    目录 引言 一.介绍 二.shared_ptr类 make_shared函数 shared_ptr的拷贝和引用 shared_ptr自动销毁所管理的对象… 使用动态生存期的资源的类 应用举例:Blob类 定义Blob类 StrBlob的拷贝.赋值和销毁 三.直接管理内存 使用new分配内存 使用new动态分配和初始化对象 动态分配const对象 内存耗尽 使用delete释放内存 基本介绍 举例 四.shared_ptr和new结合使用 new直接初始化share_ptr 初始化时传入可调用对象

  • C++11智能指针中的 unique_ptr实例详解

    在前面一篇文章中,我们了解了 C++11 中引入的智能指针之一 shared_ptr 和 weak_ptr ,今天,我们来介绍一下另一种智能指针 unique_ptr . 往期文章参考: [C++11新特性] C++11 智能指针之shared_ptr [C++11新特性] C++11智能指针之weak_ptr unique_ptr介绍 unique是独特的.唯一的意思,故名思议,unique_ptr可以"独占"地拥有它所指向的对象,它提供一种严格意义上的所有权. 这一点和我们前面介绍

  • C++学习之移动语义与智能指针详解

    移动语义 1.几个基本概念的理解 (1)可以取地址的是左值,不能取地址的就是右值,右值可能存在寄存器,也可能存在于栈上(短暂存在栈)上 (2)右值包括:临时对象.匿名对象.字面值常量 (3)const 左值引用可以绑定到左值与右值上面,称为万能引用.正因如此,也就无法区分传进来的参数是左值还是右值. const int &ref = a;//const左值引用可以绑定到左值 const int &ref1 = 10;//const左值引用可以绑定到右值 (4)右值引用:只能绑定到右值不能绑

随机推荐