介绍
C++程序设计中堆内存是一个非常频繁的操作,堆内存的申请和释放都由程序员自己管理,虽然自己管理内存提高了程序的效率,但整体来说还是比较麻烦的。使用普通指针,忘了释放很容易造成内存泄漏,二次释放、程序异常时造成泄露,使用智能指针能更好的解决这个问题。
实现原理:RAII(资源分配即初始化)
C++封装类,初始化,生命周期结束时,触发析构,自动回收空间
智能指针的四种类型
- auto_ptr
- share_ptr
- unique_ptr
- weak_ptr
实现与使用
auto_ptr
C++98标准 其中由设计缺陷,现在已不适用
(缺陷如代码所示)
C++11引如了share_ptr,unique_ptr,weak_ptr
#include<iostream>
#include<string>
#include<memory>
using namespace std;
class AA
{
public:
AA(const string s)
{
str= s;
cout<<"AA()"<<endl;
}
~AA()
{
cout<< "~AA()"<<endl;
}
void say()
{
cout<<str<<endl;
}
private:
string str;
};
int main()
{
//初始化指针指针 auto_ptr
//初始化方法1
{
std::auto_ptr<AA> ap1(new AA("using auto_ptr")); //ap1 就是智能指针
ap1->say(); //对象生命结束, 指针回收对象是自动的.
}
cout<<"------------------"<<endl;
//初始化方法2 可以通过reset()方法
{
std::auto_ptr<AA> ap1;
ap1.reset( new AA("using auto_ptr"));
ap1->say();
}
cout<<"------------------"<<endl;
//可以赋值
{
std::auto_ptr<AA> ap1(new AA("using auto_ptr")); //有可能 已经被unique_ptr取代了
std::auto_ptr<AA> ap2; // unique_ptr
ap2 = ap1; // ap2获取ap1的对象
ap1->say(); // ap2获取ap1的对象, ap1里面没对象,再调用就出问题.
}
system("pause");
return 0;
}
share_ptr
share_ptr和auto_ptr最大的区别就是,share_ptr解决了指针间共享对象所有权的问题,也就是auto_ptr中的复制的奇怪问题,所以满足了容器的要求,可以用于容器中。而auto_ptr显然禁止共享对象所有权,不可以用于容器中。
成员函数:
- 默认构造函数,函数参数为变量地址
- 拷贝构造函数,函数参数为引用
- 重载函数operator*
- 重载函数operator->
- 重载函数operator=,使之可以进行隐形转换操作,
注:实际C++源码中是没有这个的,构造函数用关键字explicit声明,在定义对象时必须显示调用初始化,不能使用赋值操作符,进行隐式转换。 - 空函数判断
- 引用次数统计函数
注:使用share_ptr也要引用#include<memory>,如下使用简答的源码实现:
#ifndef _SHARED_PTR_H
#define _SHARED_PTR_H
/*
一个模板T* ptr,指向实际的对象
一个引用次数
重载operator*和operator->,使得能像指针一样使用share_ptr
重载copy constructer,使其引用次数加一
重载operator=,如果原来的shared_ptr已经有对象
*/
template<typename T>
class shared_ptr
{
public:
shared_ptr(T* p) :count(new int(1)), _ptr(p); //默认构造函数,必须自己显式的开辟内存
shared_ptr(shared_ptr<T>& p) :count(&(++p.count)), _ptr(p._ptr); //拷贝构造函数,属于强制转换,显式
T& operator*(); //
T* operator->();
shared_ptr<T> & operator=(shared_ptr<T>& p); //对等号进行重载,保证保存同为shared_ptr的指针能相互转换,等号
//左边计数器减1,右边计数器加1。
~shared_ptr();
bool empty(); //检查是否指向一个空T
int GetCount();
private:
int* count; //引用计数器
T* _ptr; //每创建一个对象,则有一个指针指向一个shared_ptr类型
};
#endif
#include"_shared_ptr.h"
template<typename T>
shared_ptr<T>::shared_ptr(T* p = nullptr) :count(new int(1)), _ptr(p) //默认构造函数,必须自己显式的开辟内存
{
if (_ptr)
{
count = new int(1); //如果初始值不为空,则计数器为1
}
else
{
count = new int(0); //当初始值为空时,则计数器为0;
}
}
template<typename T>
shared_ptr<T>::shared_ptr(shared_ptr<T>& p) : count(&(++p.count)), _ptr(p._ptr) //拷贝构造函数,属于强制转换,显式
{
if (this != p)
{
this->_ptr = p._ptr;
this->count = p.count;
*(this->count)++;
}
}
template<typename T>
T& shared_ptr<T>::operator*()
{
//assert(this->_ptr == nullptr)
return *(this->_ptr);
}
template<typename T>
T* shared_ptr<T>::operator->()
{
//assert(this->_ptr == nullptr);
return this->_ptr;
}
template<typename T>
shared_ptr<T>& shared_ptr<T>::operator=(shared_ptr<T>& p) //对等号进行重载,保证保存同为shared_ptr的指针能
//相互转换,等号左边计数器减1,右边计数器加1。
{
++*(p.count); //等式右边引用次数加一,左边引用次数减一
if (this->_ptr && 0 == --*this->count) //当左边引用次数为零
{
delete count;
delete _ptr;
}
this->count = p.count;
this->_ptr = p._ptr;
return *this;
}
template<typename T>
shared_ptr<T>::~shared_ptr() //当诶空时,清除
{
if (0 == *(this->count))
{
delete count;
delete _ptr;
}
}
template<typename T>
bool shared_ptr<T>::empty() //检查是否指向一个空T,当为空时,记得count也为零
{
return _ptr == nullptr;
}
template<typename T>
int shared_ptr<T>::GetCount()
{
return *count;
}
unique_ptr
unique_ptr的构造函数与auto_ptr一样,构造函数采用explicit声明,防止复制/拷贝时不必要的类型转换,在定义对象时必须显示调用初始化式,不能使用赋值操作符进行隐式转换。
注:此代码未包含自定义删除器
成员函数:
- get函数:获取内部对象的指针,由于已经重载了()方法,因此和直接使用对象是一样的。
- release函数:放弃内部对象的所有权,将内部指针置空,此指针需要手动释放。
- reset函数:销毁内部对象并接受新的对象的所有权。
- 默认构造函数,函数参数为变量地址
- 拷贝构造函数,函数参数为引用
- 重载函数operator*
- 重载函数operator->
- 重载函数operator=
#ifndef UNIQUE_PTR_H
#define UNIQUE_PTR_H
template<typename T>
class _unique_ptr
{
public:
_unique_ptr(T* p = nullptr) :_ptr(p); //默认构造函数
_unique_ptr(_unique_ptr<T>& p) :_ptr(p._ptr); //拷贝构造函数
T& operator*();
T* operator->();
_unique_ptr<T>& operator=(_unique_ptr<T>& p); //赋值操作符重载
T* get();
T* release();
void reset(T* p);
private:
T * _ptr;
};
#endif
#include"unique_ptr.h"
template<typename T>
_unique_ptr<T>::_unique_ptr(T* p)
{
_ptr = p;
}
template<typename T>
_unique_ptr<T>::_unique_ptr(_unique_ptr<T>& p)
{
_ptr = p.release();
}
template<typename T>
T& _unique_ptr<T>::operator*()
{
return *(this->_ptr);
}
template<typename T>
T* _unique_ptr<T>::operator->()
{
return this->_ptr;
}
template<typename T>
_unique_ptr<T>& _unique_ptr<T>::operator=(_unique_ptr<T>& p)
{
if (p.get() != this->get())
{
delete _ptr;
}
_ptr = p._ptr;
}
template<typename T>
T* _unique_ptr<T>::get()
{
return this->_ptr;
}
template<typename T>
T* _unique_ptr<T>::release()
{
T* tmp = _ptr;
delete _ptr;
return tmp;
}
template<typename T>
void _unique_ptr<T>::reset(T* p)
{
if (p != _ptr)
{
delete _ptr;
}
_ptr = p;
}
weak_ptr
weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的 shared_ptr。 weak_ptr只是提供了对管理对象的一个访问手段。
weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。定义在 memory 文件中(非memory.h), 命名空间为 std.