智能指针详解

0.指针的危害

  • 指针未初始化
  • 野指针
  • 内存泄漏

1.智能指针分类

智能指针进入C++标准的版本头文件boost头文件说明
auto_ptrC++03memory<boost/auto_ptr.hpp>尽量避免使用
unique_ptrC++11memory<boost/unique_ptr.hpp>管理单个堆内存对象,独享所有权,不允许赋值和拷贝,不能用于管理数组对象
shared_ptrC++11memory<boost/shared_ptr.hpp>引用计数智能指针,共享所有权
weak_ptrC++11memory<boost/weak_ptr.hpp>shared_ptr的观察者,只进行引用而不改变引用计数,用来解决shared_ptr的循环引用问题
scoped_ptr--<boost/scoped_ptr.hpp>作用域智能指针,功能与unique_ptr相似。
  • 本质:

将指针封装为类对象成员,并在析构函数里删除指针指向的内存。

  • 不同:
  1. auto_ptrunique_ptrscoped_ptr马上删除。
  2. shared_ptr计数为0删除。
  3. weak_ptr不删除。

智能指针是来解决指针危害的。

1. auto_ptr

  • 作用

    对作用域内的动态分配对象的自动释放

  • 基本类型

#include <iostream>
#include <memory>
using namespace std;
int main(){
    int* pn = new int(10);
    auto_ptr<int> ap(pn);
    cout << *ap << endl;
}
  • 类类型
#include <iostream>
#include <memory>
using namespace std;
class Test{
public:
    Test(){cout << __func__ << endl;}
    ~Test(){cout << __func__ << endl;}
    void Func(){cout << __func__ << endl;}
};
int main(){
    Test* pt = new Test;
    auto_ptr<Test> apt(pt);
    apt->Func();
}

通过valgrind可以看到没有内存泄露,指针可以被正常释放。

  • 缺陷
  1. 两个auto_ptr不能同时拥有同一个对象
#include <memory>
using namespace std;
int main(){
    int* pn = new int(10);
    auto_ptr<int> ap1(pn);
    auto_ptr<int> ap2(pn);
}
  1. auto_ptr不能管理数组指针
#include <memory>
using namespace std;
int main(){
    int*pa=new int[10];
    auto_ptr<int>ap(pa);
}
  1. auto_ptr被拷贝或被赋值后,失去对原指针的管理.这种情况被称为指针所有权传递。

赋值的情况

#include <iostream>
#include <memory>
using namespace std;
int main(){
    int*p = new int(10);
    auto_ptr<int> ap1(p);
    cout<< *ap1 <<endl;
    auto_ptr<int> ap2=ap1;
    cout<< *ap1 <<endl;
}

拷贝的情况

#include <iostream>
#include <memory>
using namespace std;
void Func(auto_ptr<int> ap){
    cout << *ap << endl;
}
int main(){
    int*p = new int(10);
    auto_ptr<int> ap(p);
    cout<< *ap <<endl;
    Func(ap);
    cout<< *ap <<endl;
}

在这里插入图片描述

  1. auto_ptr不能作为容器对象,因为STL容器中的元素经常要支持拷贝,赋值等操作。
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
int main(){
    int*p = new int(10);
    auto_ptr<int> ap(p);
    vector<auto_ptr<int> > vec;
    vec.push_back(ap);
}

2. unique_ptr

  • 作用
    代替auto_ptr,不复制与赋值。

3. scoped_ptr

  • 作用
    unique_ptr相似,在本作用域中使用,不能复制与赋值。
    在这里插入图片描述

4. shared_ptr

循环引用问题

#include <string>  
#include <iostream>  
#include <boost/shared_ptr.hpp>  
#include <boost/weak_ptr.hpp>  
  
class parent;  
class children;  
  
class parent {  
public:  
    ~parent() { std::cout <<"destroying parent\n"; }  
public:  
    boost::shared_ptr<children> children;  
};  
  
class children {  
public:  
    ~children() { std::cout <<"destroying children\n"; }  
public:  
    boost::shared_ptr<parent> parent;  
};  
void test(){  
    boost::shared_ptr<parent> father(new parent());  
    boost::shared_ptr<children> son(new children);  
    father->children = son;  
    son->parent = father;  
}  
void main(){  
    std::cout<<"begin test...\n";  
    test();  
    std::cout<<"end test.\n";  
} 

5. weak_ptr

解决shared_ptr循环引用问题

#include <string>  
#include <iostream>  
#include <boost/shared_ptr.hpp>  
#include <boost/weak_ptr.hpp>  

using namespace boost; 

class parent;  
class children;  
class parent {  
public:  
    ~parent() { std::cout <<"destroying parent\n"; }  
public:  
    shared_ptr<children> children;  
};
class children {  
public:  
    ~children() { std::cout <<"destroying children\n"; }
public:  
    weak_ptr<parent> parent;  
};
void test(){  
    shared_ptr<parent> father(new parent());  
    shared_ptr<children> son(new children);  
    father->children = son;  
    son->parent = father;  
}
void main(){  
    std::cout<<"begin test...\n";  
    test();  
    std::cout<<"end test.\n";  
}  

智能指针weak_ptr主要用来协助shared_ptr。不参与引用计数,但是有以下好处:

  1. 打破递归的依赖关系
  2. 使用一个共享的资源但是不要所有权,不添加引用计数
  3. 避免悬空指针。
  • 方法一
boost::shared_ptr<std::string> sp(new std::string("method1");
// 从shared_ptr构建出来
boost::weak_ptr<std::string>wp(sp);
// 再从shared_ptr获取回去
boost::shared_ptr<std::string> p = wp.lock();
  • 方法二
boost::shared_ptr<std::string> sp(new std::string("method1");
// 从shared_ptr构建出来
boost::weak_ptr<std::string>wp(sp);
// 再从shared_ptr获取回去
boost::shared_ptr<std::string> p(wp);

总结

智能指针的特性

智能指针管理同一个对象可拷贝可复制所有权成为容器元素管理数组指针
auto_ptrNGOKOK传递NGNG
unique_ptrNGNGNG独享NGNG
scoped_ptrNGNGNG独享 NGNG
shared_ptrNGOKOK共享OKNG
weak_ptrNGOKOK共享OKNG

虽然通过弱引用指针可以有效的解除循环引用,但这种方式必须在程序员能预见会出现循环引用的情况下才能使用,也可以是说这个仅仅是一种编译期的解决方案。
如果程序在运行过程中出现了循环引用,还是会造成内存泄漏的。
因此,不要认为只要使用了智能指针便能杜绝内存泄漏。

内存管理技术

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

实践

有关智能指针的使用规则

  • Effective C++
    条款17:以独立语句将newed对象置于智能 -指针
  • Effective STL
    条款8:永不建立auto_ptr的容器
  • Exceptional C++
    条款37:auto_ptr
  • More Exceptional C++
    条款21:未管理指针存在的问题,之二:使用 auto_ptr
    条款29:使用 auto_ptr
    条款30:智能指针成员,之一:auto_ptr 存在的问题
  • Effective Modern C++
    条款18: 使用std::unique_ptr来管理独占所有权的资源
    条款19: 使用std::shared_ptr来管理共享所有权的资源
    条款20: 使用std::weak_ptr替换会造成指针悬挂的类std::shared_ptr指针
    条款21: 比起直接使用new优先使用std::make_uniquestd::make_shared

简单实现智能指针

// #include <memory>
#include <iostream>
using namespace std;

// auto_ptr C++03 C++11以后被废弃
// unique_ptr C++11 ~ scope_ptr boost 
// shared_ptr C++11/boost
// weak_ptr C++11/boost

namespace miniSTL{
template<typename T>
class auto_ptr{
public:
    typedef T value_type;
    typedef value_type* pointer;
    typedef value_type& reference;
    typedef const reference const_reference;
private:
    pointer ptr;
public:
    auto_ptr(pointer p):ptr(p){}
    auto_ptr(const auto_ptr& p):ptr(p.ptr){
       const_cast<auto_ptr&>(p).ptr = NULL; // 
    }
    auto_ptr& operator=(const auto_ptr& p){
        if(this == &p) return *this;
        delete ptr;
        ptr = p.ptr;
        const_cast<auto_ptr&>(p).ptr = NULL; // 
    }
    ~auto_ptr(){delete ptr;}
    reference operator*(){return *ptr;} 
    const_reference operator*()const{return *ptr;} 
    pointer operator->(){return ptr;}
    const pointer operator->()const{return ptr;}
};

template<typename T>
class unique_ptr{
public:
    typedef T value_type;
    typedef value_type* pointer;
    typedef value_type& reference;
    typedef const reference const_reference;
private:
    pointer ptr;
public:
    unique_ptr(pointer p):ptr(p){}
    unique_ptr(const unique_ptr& p) = delete;
    unique_ptr& operator=(const unique_ptr& p) = delete;
    ~unique_ptr(){delete ptr;}
    reference operator*(){return *ptr;} 
    const_reference operator*()const{return *ptr;} 
    pointer operator->(){return ptr;}
    const pointer operator->()const{return ptr;}
};


template<class T>
class shared_ptr{
   struct Data{
       T* ptr;
       size_t count;
       Data(T* ptr,size_t count):ptr(ptr),count(count){}
   };
   Data* data;
public:
   // explicit 取消单参构造函数等号初始化,不允许隐式转换到该对象
   explicit shared_ptr(T* ptr):data(new Data(ptr,1)){}
   ~shared_ptr(){
      data->count--;
      if(data->count == 0){
          delete data->ptr;
          delete data;
      }
   }
   shared_ptr(const shared_ptr& ptr){
       data = ptr.data;
       data->count++;
   }

   shared_ptr& operator=(const shared_ptr& ptr){
       if(this == &ptr || data == ptr.data) return *this;
       data->count--;
       if(data->count == 0){
           delete data->ptr;
           delete data;
       }
       data = ptr.data;
       data->count++;
   }
   T& operator*(){
      return *data->ptr;
   }
   T* operator->(){
      return data->ptr;
   }
   bool operator==(const shared_ptr& ptr)const{
       return data->ptr == ptr.data->ptr;
   }
   bool operator!=(const shared_ptr& ptr)const{
       return data->ptr != ptr.data->ptr;
   }
};

};

using namespace miniSTL;

void Func(auto_ptr<int> ap){
    cout << *ap << endl;
}
void Func(unique_ptr<int> up){
    cout << *up << endl;
}
void Func(shared_ptr<int> sp){
    cout << *sp << endl;
}

class IntegerPtr{
    shared_ptr<int> ptr;
public:
    IntegerPtr(int n):ptr(new int(n)){}
    void Print(){
        cout << *ptr << endl;
    }
};

int main(){
    auto_ptr<int> ap(new int(100));
//    Func(ap);
//    auto_ptr<int> ap2 = ap;
    cout << *ap << endl;

    unique_ptr<int> up(new int(100));
    // Func(up);
    // unique_ptr<int> up2 = up;
    cout << *up << endl;
    
    shared_ptr<int> sp(new int(110));
    Func(sp);
    shared_ptr<int> sp2 = sp;
    cout << *sp2 << endl;
    cout << *sp << endl;

    // 空指针解引用
    // int* p = NULL;
    // cout << *p << endl;
    IntegerPtr ptr(200);
    ptr.Print();
    IntegerPtr ptr2 = ptr;
    ptr2.Print();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值