您的位置:首页 > 编程语言 > C语言/C++

C++智能指针详解

2017-04-16 14:58 239 查看

1.智能指针的产生背景及发展历史:

在C++中动态内存的管理由一对运算符来管理:new,为动态内存分配空间并返回一个指向该对象的指针;delete,接受一个动态对象的指针,并销毁该对象,释放与之相关联的内存。有时我们忘记释放内存,造成内存泄漏;有时在有指针引用内存的情况下就释放了它,在这种情况下会产生引用非法内存的指针。


[b]RAII(一种解决问题的思想):[/b]

RAII(Resource Acquisition Is Initialization),也称为为“资源获取就是初始化”,是C++语言的一种管理资源、避免泄漏的惯用法。C++标准保证任何情况下,已构造的对象最终会销毁,即它的析构函数最终会被调用。简单的说,RAII 的做法是使用一个对象,在其构造时获取资源,在对象生命期控制对资源的访问使之始终保持有效,最后在对象析构的时候释放资源。通过应用RAII思想,就产生了智能指针:
智能纸指针的行为类似常规指针;重要的是它负责自动释放所指的对象。


发展历程:



2.模拟实现智能指针

1>模拟实现auto_ptr

template <class T>
class AutoPtr
{
public:
AutoPtr(T *ptr)
:_ptr(ptr)
{
}
~AutoPtr()
{
if (_ptr != NULL)
{
delete _ptr;
_ptr = NULL;
}
}
AutoPtr(AutoPtr <T> & ap)
:_ptr(ap._ptr)
{
ap._ptr = NULL;
//只能有一个对象的成员指针指向同一块区域,前一个对象的成员指针置为空指针
}

T& operator  //注意返回值问题不能返回一个临时变量,临时变量具有常性,会导致不能通过解引用更改值
{
return *(this->_ptr);
}

AutoPtr& operator=(AutoPtr &ap)
{
//防止自赋值
if (this != &ap)
{
delete _ptr;
this->_ptr = ap._ptr;
ap._ptr = NULL;
}
return *this;
}

T*  operator->()
{
//this->_ptr->;
return _ptr;
}
private:
T *_ptr;
};

class AA
{
public:
int _a;
int _b;
};

//测试用例

void TestAutoPtr()
{
AutoPtr<int> ap1(new int);
*ap1 = 10;
AutoPtr<int> ap2(ap1);
//*ap1 = 20;
//这句代码会导致程序崩溃,因为当有两个对象的成员指针指向同一块区域
//时,前一个对象的成员指针会被置成空指针
AutoPtr<int> ap3(new int(20));
ap2 = ap3;
AutoPtr<AA> ap4(new AA);
ap4->_a = 10;
ap4->_b = 20;
ap2 = ap3;
}


2>模拟实现scoped_ptr(和unique_ptr功能类似)

template<class T>
class ScopedPtr
{
public:
ScopedPtr(T *ptr)
:_ptr(ptr)
{
}

~ScopedPtr()
{
if (_ptr)
{
delete _ptr;
_ptr = NULL;
}
}

T& operator *()
{
return  *(_ptr);
}

T* operator ->()
{
return _ptr;
}
protected:
//构造和拷贝构造只声明,不定义
ScopedPtr& operator=(const ScopedPtr & sp);
ScopedPtr(const ScopedPtr & sp);
private:
T* _ptr;
};

class AA
{
public:
int _a;
int _b;
};

//测试用例
void TestScopedPtr()
{

ScopedPtr<int> ap1(new int);
*ap1 = 10;
ScopedPtr<int> ap3(new int(10));
//  ap3 = ap1;
//  ScopedPtr<int> ap4(ap1);
//上述两句代码编译不通过,因为ScopedPtr的构造和拷贝构造只声明,没定义,
//且为了防止别人进行修改定义为保护或者私有成员
ScopedPtr<AA> ap2(new AA);
ap2->_a = 10;
ap2->_b = 20;
}


3>shared_ptr的模拟实现(仿函数版)(实际实现要复杂)

仿函数:

仿函数(functor),就是使一个类的使用看上去象一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。
没有使用仿函数,就不能实现SharedPtr的多种功能,只使用delete删除的话,如果是new[]的空间,就会造成内存泄漏。




template<class T>
struct DeleteArray
{
void operator()(T *ptr)
{
delete[] ptr;
}

};

template&l
4000
t;class T>
struct Delete
{
void operator()(T *ptr)
{
delete ptr;
}

};

struct Fclose
{
void operator()(FILE * ptr)
{
fclose(ptr);
}
};

template<class T,class Del>
class SharedPtr
{
public:
SharedPtr(T *ptr)
:_ptr(ptr)
, _reference_count(new int (1))
{
}
~SharedPtr()
{
Clear();
}
void Clear()
{
if (--(*_reference_count) == 0)
{
_del(_ptr);
delete _reference_count;
_reference_count = NULL;
}

}
SharedPtr( SharedPtr<T,Del> & sp)
{
_ptr = sp._ptr;
_reference_count = sp._reference_count;
(*_reference_count)++;
}

//传统写法
//SharedPtr& operator =(const SharedPtr<T,Del> & sp)
//{
//  if (this != &sp)
//  {
//      Clear();
//      _ptr = sp._ptr;
//      _reference_count = sp._reference_count;
//      (*_reference_count)++;
//  }
//  return *this;
//}

//现代写法
SharedPtr& operator =(SharedPtr<T,Del> sp)
{
swap(_ptr, sp._ptr);
swap(_reference_count, sp._reference_count);
return *this;
}
T& operator *()
{
return *(this->_ptr);
}

T*  operator->()
{
return _ptr;
}
private:
T *_ptr;
Del _del;
int *_reference_count;
};

//测试用例
void TestSharedPtr()
{
SharedPtr<int,Delete<int>> ap3(new int(10));
SharedPtr<int,Delete<int>> ap4(ap3);
SharedPtr<int,DeleteArray<int>> ap5(new int[10]);
SharedPtr<FILE, Fclose>ap6(fopen("text.txt", "w"));
}


4>weak_ptr产生原因:

shared_ptr虽然功能强大,但是存在一些问题,比如说循环引用问题。


struct ListNode
{
shared_ptr<ListNode> _prev;
//shared_ptr为库文件中实现的,只需包memory即可使用
shared_ptr<ListNode> _next;
int data;
ListNode(int x)
{
data = x;
_prev = NULL;
_next = NULL;
}
~ListNode()
{
cout << "~ListNode()" << endl;
}
};
int main()
{
shared_ptr<ListNode> cur(new ListNode(1));
shared_ptr<ListNode> next(new  ListNode(2));
cur->_next = next;
next->_prev = cur;
return 0;
}


循环引用:



为了解决shared_ptr循环引用所带来的问题,就有了weak_ptr:

weak_ptr是一种不控制所指对象生存周期的智能指针,它指向一个shared_ptr所管理的对象。将一个weak_ptr绑定到shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。weak_ptr的名字抓住了这种智能指针“弱”共享对象的特点。


3.智能指针的总结

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息