智能指针:存储指向动态分配对象指针的类。
满足的条件:具有RAII思想,能够像指针一样(运算符的重载,解引用,指向对象成员),正确的资源管理。
RAII思想(资源分配及初始化):定义一个类来封装资源的分配和释放,构造函数中完成资源的分配和初始化,析构函数中完成资源的清理,可以保证资源的正确初始化和释放。
引入智能指针的目的是什么呢?
这里给出一段代码:
void func()
{
int *_ptr = new int(3);
if (_ptr)
{
throw 3;
}
delete _ptr;
}
int main()
{
try
{
func();
}
catch (...)
{}
return 0;
}
上述代码在func函数中new一个四字节空间并进行初始化,接下来if条件判断语句为真,抛出异常,main函数直接捕获异常,函数返回0,导致开辟的空间没有被释放,造成内存泄漏。
其实只要在throw之前加一个delete语句就可以解决问题,但是重点是当代码超级多的时候,肯定会忘记的。那么有没有一种方法是可靠的呢——”智能指针“。
智能指针的发展历史
C++98 : auto_ptr 在构造对象的时候获取对象的管理权(管理权转移),
带有缺陷。“不提倡使用”
boost库(非官方):scoped_ptr 防拷贝的守卫指针(简单粗暴)。—scoped_array
shared_ptr 加入引用计数的共享指针(复杂,有循环引用的缺陷)。—shared_array
weak_ptr 不能单独存在的弱指针(辅助解决shared_ptr循环引用的问题)
C++11 (官方库):沿用了boost库,将scoped_ptr改名为unique_ptr,shared_ptr和weak_ptr名字没变。
对operator*,operator->的理解
template<class T>
class Myclass
{
public:
Myclass(T* ptr)
:_ptr(ptr)
{}
Myclass(Myclass<T>& p)
{}
Myclass<T>& operator=(Myclass<T>& p)
{}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
~Myclass()
{
if (_ptr)
{
delete _ptr;
}
}
private:
T* _ptr;
};
struct My
{
int a = 3;
int b = 6;
};
int main()
{
Myclass<int> p1(new int);
*p1 = 20;
int tmp = p1.operator*();
Myclass<My> p2(new My);
cout << "p1: " << *p1 << endl << endl;
cout << "tmp: " << tmp << endl << endl;
cout << "p2->a: " << p2->a << endl << endl;
cout << "p2.operator->()->a: " << p2.operator->()->a << endl << endl;
cout << "p2->b: " << p2->b << endl << endl;
cout << "p2.operator->()->b: " << p2.operator->()->b << endl << endl;
system("pause");
}
详解auto_ptr:
auto_ptr事实上是一个类,在构造时获取对象的管理权,不用考虑啥时候释放动态开辟的空间,在析构函数中直接释放,不会出现内存泄露的问题。
//auto_ptr的模拟实现
template <class T>
class Auto_ptr
{
public:
Auto_ptr(T* ptr)//构造函数
:_ptr(ptr)
{}
Auto_ptr(Auto_ptr<T>& ap)//拷贝构造函数
: _ptr(ap._ptr)
{
ap._ptr = NULL;
}
Auto_ptr<T>& operator=(Auto_ptr<T>& ap)//赋值运算符的重载
{
if (this != &ap)
{
if (_ptr)
{
delete _ptr;
}
_ptr = ap._ptr;
ap._ptr = NULL;
}
return *this;
}
~Auto_ptr()//析构函数
{
if (_ptr)
{
delete _ptr;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T* _ptr;
};
struct AA
{
int _a;
int _b;
};
int main()
{
Auto_ptr<int> ap(new int (3));
*ap = 5;
Auto_ptr<int> ap1(ap);
Auto_ptr<int> ap3(ap);//一个auto_ptr被拷贝或被赋值后, 其已经失去对原对象的所有权指向NULL
Auto_ptr<AA> ap2(new AA);
ap2->_a = 2;
ap2->_b = 3;
return 0;
}
缺陷:
1.一个指针变量指向的空间不能由两个auto_ptr管理,不然会析构两次,程序崩溃;
//错误
int *ptr=new int(5);
auto_ptr<int> ap(ptr);
auto_ptr<int> ap(ptr);
2.auto_ptr不能用来管理数组,析构函数中用的是delete。
int *ptr=new int[6];
auto_ptr<int> ap(ptr);
详解scoped_ptr
scoped_ptr防拷贝(拷贝构造函数和赋值运算符的重载是只声明不实现;用private对其进行访问限定,防止在类外定义)直接明了。
//scoped_ptr的模拟实现
template <class T>
class Scoped_ptr
{
public:
Scoped_ptr(T* ptr)
:_ptr(ptr)
{}
~Scoped_ptr()
{
if (_ptr)
{
delete _ptr;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
Scoped_ptr(const Scoped_ptr<T>& sp);
Scoped_ptr<T>& operator=(const Scoped_ptr<T>& sp);
T* _ptr;
};
int main()
{
Scoped_ptr<int> sp(new int(2));
return 0;
}
缺陷:不能拷贝,管理的对象不能共享所有权,功能不全面。
scoped_array
scoped_array和scoped_ptr的功能是一样的,只是scoped_array管理的是数组,需要像数组一样。
//scoped_array的模拟实现
template <class T>
class Scoped_array
{
public:
Scoped_array(T* ptr)
:_ptr(ptr)
{}
~Scoped_array()
{
if (_ptr)
{
delete[] _ptr;
}
}
T& opeartor[](size_t i)
{
return _ptr[i];
}
private:
Scoped_array(const Scoped_array<T>& sa);
Scoped_array<T>& operator=(const Scoped_array& sa);
T* _ptr;
};
int main()
{
Scoped_array<int> sa(new int[3]);
return 0;
}
详解shared_ptr
//shared_ptr模拟实现
template <class T>
class Shared_ptr
{
public:
Shared_ptr(T* ptr == NULL)
:_ptr(ptr)
, _refCount(new int(1))
{}
Shared_ptr(const Shared_ptr<T>&sp)
:_ptr(sp._ptr)
,_refCount(sp._refCount)
{
*_refCount++;
}
Shared_ptr<T>&operator=(const Shared_ptr<T>& sp)
{
if (_ptr != &sp._ptr)
{
if (--(*_refCount) == 0)
{
delete _ptr;
delete _refCount;
}
_ptr = sp._ptr;
_refCount = sp._refCount;
(*_refCount)++;
}
return *this;
}
~Shared_ptr()
{
if (--(*_refCount) == 0)
{
delete _ptr;
delete _refCount;
}
}
T& operator*()
{
return *_ptr;
}
T* opeartor->()
{
return _ptr;
}
int Count()
{
return *_refCount;
}
private:
T* _ptr;
int *_refCount;
};
Shared_ptr会出现循环引用:
//测试代码
struct ListNode
{
int _data;
Shared_ptr<ListNode> _next;
Shared_ptr<ListNode> _prev;
ListNode(int x)
:_data(x)
, _next(NULL)
, _prev(NULL)
{}
~ListNode()
{}
};
int main()
{
Shared_ptr<ListNode> cur(new ListNode(1));
Shared_ptr<ListNode> next(new ListNode(2));
cur->_next = next;
next->_prev = cur;
cout << "cur._refCount " <<cur.Count()<<endl;
cout << "next._refCount "<<cur.Count()<<endl;
system("pause");
}
为什么每个节点都有两个引用计数呢?这就是循环引用的问题。
为了解决循环引用问题,这里需引入weak_ptr.
详解weak_ptr
weak_ptr是和shared_ptr配合使用的,weak_ptr不增加节点的引用计数。
//weak_ptr的模拟实现
template <class T>
class Weak_ptr
{
public:
Weak_ptr()
:_ptr(NULL)
{}
Weak_ptr(const Shared_ptr<T>& sp)
:_ptr(sp._ptr)
{}
Weak_ptr<T>& operator=(const Shared_ptr<T>& sp)
{
_ptr = sp._ptr;
return *this;
}
~Weak_ptr()
{
if (_ptr)
{
delete _ptr;
}
}
private:
T* _ptr;
};
那么weak_ptr是如何解决循环引用问题?
struct ListNode
{
int _data;
Weak_ptr<ListNode> _next;
Weak_ptr<ListNode> _prev;
ListNode(int x)
:_data(x)
, _next()
, _prev()
{}
~ListNode()
{}
};
int main()
{
Shared_ptr<ListNode> cur(new ListNode(3));
Shared_ptr<ListNode> next(new ListNode(4));
cur->_next = next;
next->_prev = cur;
cout << "cur._refCount " << cur.Count() << endl;
cout << "next._refCount "<<cur.Count()<<endl;
system("pause");
}