一、概述
智能指针是一个类,主要用于管理堆上分配的内存,它将普通的指针封装成一个栈对象。在栈对象的生命周期结束后,会在析构函数中释放申请的内存,防止内存泄漏。
在使用智能指针的时候,需要引入头文件<memory>。
1、作用:
智能指针是管理一个普通指针,在函数结束时自动释放内存空间,不需要手动释放内存空间。避免申请的内存空间在函数结束时忘记释放,造成的内存泄漏。
2、智能指针类型
1、auto_ptr:
c++98的方案,c++11已经抛弃,采用所有权模式——(一个对象只能对应一个auto_ptr 指针,当拷贝时,会将对象转移到新的指针所持有,之前的指针不再持有)。
void testAutoPtrFunc()
{
auto_ptr<string> p1 (new string("This is a auto_ptr!"));
cout << *p1 << endl;
auto_ptr<string> p2;
p2 = p1;
cout << *p2 << endl;
// cout << *p1 << endl; //此时再次访问p1会报错
}
2、unique_ptr:
unique_ptr 实现独占式拥有,保证同一时间内只有一个智能指针可以指向该对象。也是采用所有权模式,但它可以避免内存空间泄漏,因为它针对于(new创建的对象,忘记delete)在编译期就会直接报错,不会在运行期埋下隐患。
void testUniquePtrFunc()
{
unique_ptr<string> p1 (new string("This is a unique_ptr!"));
cout << *p1 << endl;
unique_ptr<string> p2;
// p2 = p1;//编译期直接报错
p2 = move(p1);//这样才能赋值,所有权转移
cout << "move p2:" << *p2 << endl;
p1 = unique_ptr<string> (new string("Assignment new value!"));
cout << "p1 new value:" << *p1 << endl;
cout << "p2 new value:" << *p2 << endl;
unique_ptr<string> p3;
p3 = unique_ptr<string> (new string("Should assignment value!"));
cout << *p3 << endl;
}
3、shared_ptr:
shared_ptr 实现共享式拥有模式。多个智能指针可以指向相同的对象,此对象会与其相关资源在最后一个引用被销毁后释放。它使用计数机制来表示资源被几个指针共享。shared_ptr是为了解决auto_ptr在对象所有权上的局限性,使用引用计数的机制来实现共享所有权的智能指针。shared_ptr内部的引用计数是线程安全的,但是对象的读取需要加锁保护。
(1)、成员函数
a)、use_count:返回引用计数的个数;
b)、unique:返回是否是独占所有权(use_count 为1);
c)、swap:交换两个shared_ptr对象;
d)、reset:放弃内部对象的所有权或拥有对象的变更,会引起原有对象的引用计数的减少;
e)、get:返回内部对象(指针),由于已经重载()方法,因此和直接使用对象是一样的。
void testSharedPtrFunc()
{
string *str = new string("This is String!");
shared_ptr<string> p1(str);
shared_ptr<string> p2;
p2 = p1;
cout << "p1 use_count:" << p1.use_count() << endl;
cout << "p2 use_count:" << p2.use_count() << endl;
cout << "p1 所有权的值:" << p1.unique() << endl;
string *str3 = new string("This is String three!");
shared_ptr<string> p3(str3);
cout << "p1 swap before:" << p1.get() << endl;
cout << "p3 swap before:" << p3.get() << endl;
swap(p1, p3);
cout << "p1 swap after:" << p1.get() << endl;
cout << "p3 swap after:" << p3.get() << endl;
cout << "after p1 use_count:" << p1.use_count() << endl;
cout << "after p2 use_count:" << p2.use_count() << endl;
cout << "before set value p1:" << p1.get() << endl;
cout << "before set value p2:" << p2.get() << endl;
p2 = p1;
cout << "after set value p1:" << p1.get() << endl;
cout << "after set value p2:" << p2.get() << endl;
cout << "after new p1 use_count:" << p1.use_count() << endl;
cout << "after new p2 use_count:" << p2.use_count() << endl;
p1.reset();
cout << "reset p1 use_count:" << p1.use_count() << endl;
cout << "reset p2 use_count:" << p2.use_count() << endl;
}
4、weak_ptr:
weak_ptr是一种不控制对象生命周期的智能指针(即弱引用,不强持有对象,引用计数不会增加),它指向一个shared_ptr管理的对象。weak_ptr的目的是打破shared_ptr引起的循环引用,导致内存泄漏的问题。因为两个shared_ptr的对象,相互引用了,那么这两个指针的引用计数永远不会为0,从而内存资源也得不到释放,因此就造成了内存泄漏的风险。
(1)、weak_ptr 与 shared_ptr 之间相互转化
由于不能通过weak_ptr直接访问对象的方法,因此需要将它转化为shared_ptr才能访问对象的方法。weak_ptr没有重载*和->,但可以使用lock获取一个可用的shared_ptr对象。weak_ptr支持拷贝或赋值,但不会影响对应的shared_ptr内部对象的引用计数。
注意:weak_ptr在使用前需要检查其合法性(判断是否已经被释放了)。
expired():用于检查所管理的对象是否已经释放,若已释放,则返回true,否则返回false;
use_count():返回与shared_ptr共享的对象的引用计数;
reset():将weak_ptr置空。
class B;
class A
{
public:
//这样会造成循环引用,导致内存泄漏
// shared_ptr<B> _pb;
//使用weak_ptr 弱引用可以打破循环引用,避免内存泄漏
weak_ptr<B> _pb;
~A()
{
cout << "A delete \n";
}
};
class B
{
public:
shared_ptr<A> _pa;
~B()
{
cout << "B delete \n";
}
void print()
{
cout << "This is Print!" << endl;
}
};
//MARK: -- Test weak_ptr function
void testWeakPtrFunc()
{
shared_ptr<B> pb(new B());
shared_ptr<A> pa(new A());
cout << "pb use_count:" << pb.use_count() << endl;
cout << "pa use_count:" << pa.use_count() << endl;
pb->_pa = pa;
pa->_pb = pb;
cout << "after pb use_count:" << pb.use_count() << endl;
cout << "after pa use_count:" << pa.use_count() << endl;
//weak_ptr 访问对象的方法
//先检查weak_ptr的合法性
if (!pa->_pb.expired())
{
// pa->_pb->print(); //编译报错
shared_ptr<B> tmpP = pa->_pb.lock();
tmpP->print();
}
}
5、shared_ptr 和 weak_ptr的实现原理:
/*
shared_ptr 和 weak_ptr的实现原理
*/
//MARK: -- Counter 简单实现
class Counter
{
public:
Counter() : s(0), w(0){};
int s; //shared_ptr 的引用计数
int w; //weak_ptr 的引用计数
};
//MARK: -- shared_ptr 的简单实现
template <class T>
class WeakPtr; //为了用weak_ptr的lock(), 来生成shared_ptr使用,需要拷贝构造
template <class T>
class SharedPtr
{
public:
SharedPtr(T *p = 0) : _ptr(p)
{
cnt = new Counter();
if (p)
{
cnt->s = 1;
}
cout << "In construct" << cnt->s << endl;
}
~SharedPtr()
{
release();
}
SharedPtr(SharedPtr<T> const &s)
{
cout << "In s copy con" << endl;
_ptr = s._ptr;
s.cnt->s++;
cout << "Copy s construct" << s.cnt->s << endl;
cnt = s.cnt;
}
SharedPtr(WeakPtr<T> const &w)
{
cout << "In w copy con" << endl;
_ptr = w._ptr;
w.cnt->s++;
cout << "Copy w construct" << w.cnt->s << endl;
cnt = w.cnt;
}
SharedPtr<T> &operator=(SharedPtr<T> &s)
{
if (this != &s)
{
release();
s.cnt->s++;
cout << "assign construct" << s.cnt->s << endl;
cnt = s.cnt;
_ptr = s._ptr;
}
return *this;
}
T &operator*()
{
return *_ptr;
}
T &operator->()
{
return _ptr;
}
friend class WeakPtr<T>; //方便weak_ptr 与 shared_ptr设置引用计数和赋值
protected:
void release()
{
cnt->s--;
cout << "Release" << cnt->s << endl;
if (cnt->s < 1)
{
delete _ptr;
if (cnt->w < 1)
{
delete cnt;
cnt = nullptr;
}
}
}
private:
T *_ptr;
Counter *cnt;
};
//MARK: -- weak_ptr 简单实现
template <class T>
class WeakPtr
{
public:
WeakPtr()
{
_ptr = 0;
cnt = 0;
}
WeakPtr(SharedPtr<T> &s) : _ptr(s._ptr), cnt(s.cnt)
{
cout << "w con s" << endl;
cnt->w++;
}
~WeakPtr()
{
release();
}
WeakPtr<T> &operator = (WeakPtr<T> &w)
{
if (this != &w)
{
release();
cnt = w.cnt;
cnt->w++;
_ptr = w._ptr;
}
return *this;
}
WeakPtr<T> &operator = (SharedPtr<T> &s)
{
cout << "w = s" << endl;
release();
cnt = s.cnt;
cnt->w++;
_ptr = w._ptr;
return *this;
}
SharedPtr<T> lock()
{
return SharedPtr<T>(*this);
}
bool expired()
{
if (cnt)
{
if (cnt->s > 0)
{
cout << "empty!" << cnt->s << endl;
return false;
}
}
return true;
}
//方便weak_ptr与share_ptr设置引用计数和赋值
friend class SharedPtr<T>;
protected:
void release()
{
if (cnt)
{
cnt->w--;
cout << "WeakPtr release" << cnt->w << endl;
if (cnt->w < 1 && cnt->s < 1)
{
cnt = nullptr;
}
}
}
private:
T *_ptr;
Counter *cnt;
};