目录
智能指针是一种预防型的内存泄漏的解决方案。智能指针在C++没有垃圾回收器的环境下,可以很好的解决异常安全等带来的内存泄露问题。
RAII是一种利用对象生命周期来控制程序资源的简单技术。
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。借此没玩们实际上把管理一份资源的责任托管给了一个对象。这种做法有两大好处:
auto_ptr的实现原理:管理权转移的思想
template<class T> class AutoPtr { public: AutoPtr(T* ptr = NULL) :_ptr(ptr) {} ~AutoPtr() { if(_ptr) delete _ptr; } //一旦发生拷贝,就将ap中资源转移到当前对象中,然后令ap与其所管理资源断开联系 //这样就解决了一块空间被多个对象使用而造成程序奔溃问题 AutoPtr(AutoPtr<T>& ap) :_ptr(ap._ptr) { ap._ptr = NULL; } AutoPtr<T>& operator=(AutoPtr<T>& ap) { if(this != &ap)//检测是否给自己赋值 { //释放当前对象中的资源 if(_ptr) delete _ptr; //转移ap中的资源到当前对象中 _ptr = ap._ptr; ap._ptr = NULL; } return *this; } T* operator*(){return *_ptr;} T& operator->(){return _ptr;} private: T* _ptr; };
auto_ptr缺点:拷贝后会把原来对象的指针赋空了,导致ap对象悬空,通过原来的对象访问资源时就会出现问题。
unique_ptr的实现原理:防拷贝
template<class T> class UniquePtr { public: UniquePtr(T * ptr = nullptr) : _ptr(ptr) {} ~UniquePtr() { if (_ptr) delete _ptr; } T& operator*() { return *_ptr; } T* operator->() { return _ptr; } private: // C++98防拷贝的方式:只声明不实现+声明成私有 //UniquePtr(UniquePtr<T> const &); //UniquePtr & operator=(UniquePtr<T> const &); // C++11防拷贝的方式:delete UniquePtr(UniquePtr<T> const &) = delete; UniquePtr & operator=(UniquePtr<T> const &) = delete; private: T * _ptr; };
shared_ptr的原理:是通过引用计数的方式来实现多个shared_ptr对象之间共享资源
#include <thread> #include <mutex> template <class T> class SharedPtr { public: SharedPtr(T* ptr = nullptr) : _ptr(ptr), _pRefCount(new int(1)), _pMutex(new mutex) { // 如果是一个空指针对象,则引用计数给0 if (_ptr == nullptr) *_pRefCount = 0; } ~SharedPtr() { Release(); } //拷贝 SharedPtr(const SharedPtr<T>& sp) : _ptr(sp._ptr), _pRefCount(sp._pRefCount), _pMutex(sp._pMutex) { // 如果是一个空指针对象,则不加引用计数,否则才加引用计数 if (_ptr) AddRefCount(); } // sp1 = sp2 SharedPtr<T>& operator=(const SharedPtr<T>& sp) { //if (this != &sp) if (_ptr != sp._ptr) { // 释放管理的旧资源 Release(); // 共享管理新对象的资源,并增加引用计数 _ptr = sp._ptr; _pRefCount = sp._pRefCount; _pMutex = sp._pMutex; if (_ptr) AddRefCount(); } return *this; } T& operator*() { return *_ptr; } T* operator->() { return _ptr; } int UseCount() { return *_pRefCount; } T* Get() { return _ptr; } int AddRefCount() { // 加锁或者使用加1的原子操作 _pMutex->lock(); ++(*_pRefCount); _pMutex->unlock(); return *_pRefCount; } int SubRefCount() { // 加锁或者使用减1的原子操作 _pMutex->lock(); --(*_pRefCount); _pMutex->unlock(); return *_pRefCount; } private: void Release() { // 引用计数减1,如果减到0,则释放资源 if (_ptr && SubRefCount() == 0) { delete _ptr; delete _pRefCount; } } private: int* _pRefCount; // 引用计数 T* _ptr; // 指向管理资源的指针 mutex* _pMutex; // 互斥锁 };
weak_ptr可以解决这个问题,weak_ptr支持把一个shared_ptr对象赋给weak_ptr对象(赋值和拷贝构造的参数写成shared_ptr,重载),
weak_ptr不会增加引用计数,weak_ptr,析构函数不释放资源,不是RAII,weak_ptr是shared_ptr的小保姆,收拾循环引用的,但是这种方式是被动的,需要程序员来写,它自己并不会自动来管理。
struct ListNode { int _data; weak_ptr<ListNode> _prev; weak_ptr<ListNode> _next; //shared_ptr<ListNode> _prev; //shared_ptr<ListNode> _next; ~ListNode() { cout << "~ListNode()" << endl; } }; int main() { shared_ptr<ListNode> node1(new ListNode); shared_ptr<ListNode> node2(new ListNode); cout << node1.use_count() << endl; cout << node2.use_count() << endl; node1->_next = node2; node2->_prev = node1; cout << node1.use_count() << endl; cout << node2.use_count() << endl; //system("pause"); return 0; }
不是new出来的对象,shared_ptr设计了一个删除器来解决这个问题。
假设对象是malloc出来的,构造函数还可以传一个删除器的东西,一个仿函数。
template<class T> struct FreeFunc { void operator()(T* ptr) { cout << "free:" << ptr << endl; free(ptr); } }; template<class T> struct DeleteArrayFunc { void operator()(T* ptr) { cout << "delete[]" << ptr << endl; delete[] ptr; } }; int main() { FreeFunc<int> freeFunc; shared_ptr<int> sp1((int*)malloc(4), freeFunc); DeleteArrayFunc<int> deleteArrayFunc; shared_ptr<int> sp2((int*)malloc(4), deleteArrayFunc); return 0; }
boost是C++的一个非官方的第三方库,产生了很多有用的东西
RAII思想除了可以用来设计智能指针,还可以用来设计守卫锁,防止异常安全导致的死锁问题。加锁到解锁中间抛异常,break,return
设计出一个类,构造函数的时候,就锁住,析构函数里解锁
#include <thread> #include <mutex> #include <iostream> using namespace std; // C++11的库中也有一个lock_guard,下面的LockGuard造轮子其实就是为了学习他的原理 template<class Mutex> class LockGuard { public: LockGuard(Mutex& mtx) :_mutex(mtx) { _mutex.lock(); } ~LockGuard() { _mutex.unlock(); } LockGuard(const LockGuard<Mutex>&) = delete; private: // 注意这里必须使用引用,否则锁的就不是一个互斥量对象 Mutex& _mutex; }; mutex mtx; static int n = 0; void Func() { for (size_t i = 0; i < 1000000; ++i) { LockGuard<mutex> lock(mtx); ++n; } } int main() { int begin = clock(); thread t1(Func); thread t2(Func); t1.join(); t2.join(); int end = clock(); cout << n << endl; cout <<"cost time:" <<end - begin << endl; return 0; }
如果类内的锁不是引用,构造函数锁的才是传过来的锁。
只锁某一行,给一个{ }的局部域就好了