C++智能指针

1 使用智能指针的原因
  我们知道C/C++的指传统针可以用来通过指向对象地址来间接访问该对象,但是当某个指针指向人为申请的堆空间,我们时常忘记手动释放该空间从而造成内存泄漏。我们可以看看下面的代码:

void outputStr(std::string & str)
{
    std::string *ps = new std::string(str);  
    std::cout<<*ps<<endl;
    ...

    return;
}

  上面的程序我们可以清晰的看出该函数输出结果,但是并没有手动释放堆内存所以会造成内存泄漏。所以需要在return之前加入代码delete ps;
  但是有经验的工程师都知道:凡涉及“别忘了”的解决方法,很少是最佳的。
  我们来看看下面的变体:

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

  当异常发生时,delete不会被执行,同样会内存泄露。是时候智能指针出场了,aoto_ptr、shared_ptr、unique_ptr。 aoto_ptr是C++98中提供的,c++11已经将其摒弃了。
2 智能指针的介绍
  智能指针(smart pointer)是存储指向动态分配(堆)对象指针的类,用于生存期控制,能够确保自动正确的销毁动态分配的对象,防止内存泄露。
  智能指针类通过显示有参构造将new获得的地址复制给智能指针。当智能指针过期时其析构函数将使用delete来释放内存(例如智能指针类在某个函数中局部构造了一个其对象,那么系统会在跳出函数时调用析构函数,而析构函数中系统有delete上面new的地址的语句)。无需记住稍后释放这些内存,在智能指针过期时,这些内存将自动被释放。
  它的一种通用实现技术是使用引用计数(reference count)。智能指针类将一个计数器与类指向的对象相关联,引用计数跟踪该类有多少个对象共享同一指针。每次创建类的新对象时,初始化指针并将引用计数置为1;当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数;对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数;调用析构函数时,构造函数减少引用计数(如果引用计数减至0,则删除基础对象)。也就是说当多个智能指针对象有共享同一个指针的时候,当系统依次析构这些对象的时候,引用计数依次减一,直到引用计数变为0,也就是最后一个智能指针对象去释放该空间,前面释放的对象只起到减小引用计数的作用。
  智能指针就是模拟指针动作的类。所有的智能指针都会重载 -> 和 * 操作符。智能指针还有许多其他功能,比较有用的是自动销毁。这主要是利用栈对象的有限作用域以及临时对象(有限作用域实现)析构函数释放内存。当然,智能指针还不止这些,还包括复制时可以修改源对象等。智能指针根据需求不同,设计也不同(写时复制,赋值即释放对象拥有权限、引用计数等,控制权转移等)。
3 智能指针的使用
  使用智能指针的步骤如下:
  ①. 包含对应的头文件:

#include<memory>

  ②. 显示构造智能指针类的对象,并复制一个堆空间给它:

aoto_ptr<double> pd(new double);
auto_ptr<string> ps(new string);

unique_ptr<double> pdu(new double);

shared_ptr<string> pss(new string);

  那如何修改上面的程序,让普通指针变为智能指针来管理呢?步骤如下:
  ①. 包含头文件memory;
  ②. 将指向string的指针替换为指向string的智能指针对象;
  ③. 删除delete语句。
  具体实现如下:

#include<memory>
void remodel(std::string & str)
{
    std::auto_ptr<std::string> ps(new std::string(str));
    ...
    if(...)
        throw exception();
    //delete ps;
    return;
}

4 智能指针的显示构造
  我们可以看一下auto_ptr如何定义的:

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

  智能指针的构造函数使用了explicit关键字。 explicit关键字只能用于修饰只有一个参数的类的构造函数,它的作用是表面该构造函数是显示的,而非隐式的。可以阻止不应该允许的经过转换构造函数进行的隐式转换的发生。
  我们看一下下面的使用示例:

shared_ptr<double> pd;

double *p_reg = new double;
pd = p_reg;   //错误,不允许隐式转换

pd = shared_ptr<double>(p_reg);//正确

shared_ptr<double> pshared = p_reg;//错误,不允许隐式转换

shared_ptr<double> pshared(p_reg); //正确

5 切忌把智能指针用于非堆内存
  我们知道由于智能指针类析构函数中有释放赋予它地址的操作,一旦我们对一段非堆内存释放将会发生系统报错。如下:

string vacation("hello world");

shared_ptr<string>pvac(&vacation);

  当pvac过期时,程序将把delete运算符用于非堆内存,这将导致错误。
6 三种智能指针类的区别

#include <iostream>
#include <string>
#include <memory>
using namespace std;
int main()
{
    auto_ptr<string>
    films[5] = {
        auto_ptr<string> (new string("Fowl Balls")),
        auto_ptr<string> (new string("Duck Walks")),
        auto_ptr<string> (new string("Chicken Runs")),
        auto_ptr<string> (new string("Turkey Errors")),
        auto_ptr<string> (new string("Goose Eggs"))
};
    auto_ptr<string> pwin;
    pwin = films[2]; // films[2] loses ownership. 将所有权从 films[2]转让给pwin,此时films[2]不再引用该字符串从而变成空指针

    //unique_ptr<string> pwin; //编译错误
    //pwin = films[2];
    //shared_ptr<string> pwin; //没有问题!

    cout << "The nominees for best avian baseballl film are\n";
    for(int i = 0; i < 5; ++i)
        cout << *films[i] << endl;
    cout << "The winner is " << *pwin << endl;

    return 0;
}

  当使用shared_ptr的时候,pwin和films[2]指向同一个对象,而引用计数从1增加到了2。在程序末尾return后,pwin首先调用其析构函数,该析构函数将引用计数降低到1,所以不会有问题。 即 :
  使用auto_ptr 所有权转让,运行将会有错误 ;
  使用shared_ptr 指向同一对象,引用计数增加,不会有问题 ;
  使用unique_ptr 采用所有权模型,编译就会有错误。
  因此我们也可以看出unique_ptr优于auto_ptr的一个原因:
  unique_ptr比auto_ptr更安全
  还有一个原因就是:
  auto_ptr只能与new一起使用,不能与new[]一起使用(share_ptr同样)。
  unique_ptr可以使用new,也可以使用new[] 。
  分析了这么多,那么归根到底,我们如何选择呢?
7 如何选择哪种智能指针
  (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())


  参考文献:
  http://blog.csdn.net/wangshubo1989/article/details/48337955
  http://blog.csdn.net/hackbuteer1/article/details/7561235

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
C++智能指针是一种用于管理动态分配的内存资源的工具。C++中提供了多种类型的智能指针,其中包括shared_ptr、unique_ptr和weak_ptr。这些智能指针都位于头文件<memory>中。 其中,shared_ptr是一种引用计数智能指针,它允许多个智能指针共享同一个对象。shared_ptr通过对对象的引用计数来管理内存的释放,当引用计数为0时,即没有智能指针指向该对象时,对象会被自动释放。使用shared_ptr需要包含头文件<memory>,并通过new关键字创建动态分配的对象并将其交给shared_ptr进行管理。 另一种智能指针是unique_ptr,它是一种独占型智能指针,只能有一个智能指针拥有对对象的所有权。当unique_ptr对象被销毁时,它所管理的对象也会被自动释放。unique_ptr提供了更高效的内存管理方式,因为它不需要进行引用计数。使用unique_ptr也需要包含头文件<memory>,并使用new关键字创建动态分配的对象并将其交给unique_ptr进行管理。 除了shared_ptr和unique_ptr,还有其他类型的智能指针,如weak_ptr和scoped_ptr。weak_ptr是一种弱引用智能指针,它用于解决shared_ptr可能出现的循环引用问题。scoped_ptr是一种简单的智能指针,它只能在创建时初始化,并且不能进行复制和赋值操作。scoped_ptr在其所属的作用域结束时自动释放所管理的对象。 总结起来,C++智能指针是一种用于管理动态分配的内存资源的工具,包括shared_ptr、unique_ptr、weak_ptr和scoped_ptr等类型。它们提供了一种更安全、更高效的内存管理方式,避免了手动释放内存的麻烦。要使用这些智能指针,需要包含头文件<memory>,并通过new关键字创建动态分配的对象并将其交给相应的智能指针进行管理。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [C++ -- 智能指针C++11与boost库的智能指针及其使用)](https://blog.csdn.net/xu1105775448/article/details/80625936)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [C++智能指针的底层实现原理](https://blog.csdn.net/ArtAndLife/article/details/120793343)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值