目录
(1)C++98库中提供了auto_ptr ,原理是实现管理权转移
(1)C++11中开始提供更靠谱的并且支持拷贝的shared_ptr
(1)线程不安全测试,这里我们自己实现的share_ptr未加锁
- int div()
- {
- int a, b;
- cin >> a >> b;
- if (b == 0)
- throw invalid_argument("除0错误");
-
- return a / b;
- }
-
- void func()
- {
- int* p1 = new int;
-
- cout << div() << endl; //这里可能会抛异常
-
- delete p1;
- }
-
- int main()
- {
- try
- {
- func();
- }
- catch (const exception& e)
- {
- cout << e.what() << endl;
- }
-
- //Do something ...
-
- return 0;
- }
①堆内存泄漏(Heap leak)
②系统资源泄漏
总结: 内存泄漏非常常见,解决方案分为两种
实现智能指针时需要考虑以下三个方面的问题:
*
和->
运算符进行重载,使得该对象具有像指针一样的行为。- // RAII
- // 用起来像指针一样
- template<class T>
- class SmartPtr
- {
- public:
- SmartPtr(T* ptr)
- :_ptr(ptr)
- {}
-
- ~SmartPtr()
- {
- cout << "delete:" << _ptr << endl;
- delete _ptr;
- }
-
- // 像指针一样使用
- T& operator*()
- {
- return *_ptr;
- }
-
- T* operator->()
- {
- return _ptr;
- }
-
- private:
- T* _ptr;
- };
对于当前实现的SmartPtr类,如果用一个SmartPtr对象来拷贝构造另一个SmartPtr对象,或是将一个SmartPtr对象赋值给另一个SmartPtr对象,都会导致程序崩溃
- int main()
- {
- SmartPtr<int> sp1(new int);
- SmartPtr<int> sp2(sp1); //拷贝构造
-
- SmartPtr<int> sp3(new int);
- SmartPtr<int> sp4(new int);
- sp3 = sp4; //拷贝赋值
-
- return 0;
- }
- int main()
- {
- std::auto_ptr<int> sp1(new int);
- std::auto_ptr<int> sp2(sp1); //管理权的转移
-
- //最大的问题在于 sp1悬空
- *sp2 = 10;
- cout << *sp2 << endl; //10
- cout << *sp1 << endl; //error
-
- return 0;
- }
- template<class T>
- class auto_ptr
- {
- public:
- auto_ptr(T* ptr)
- :_ptr(ptr)
- {}
-
- auto_ptr(auto_ptr
& sp) - :_ptr(sp._ptr)
- {
- // 管理权转移
- sp._ptr = nullptr;
- }
-
- auto_ptr
& operator=(auto_ptr& sp) - {
- if (this != &ap)
- {
- delete _ptr; //释放自己管理的资源
- _ptr = sp._ptr; //接管sp对象的资源
- sp._ptr = nullptr; //管理权转移后sp被置空
- }
-
- return *this;
- }
-
-
- ~auto_ptr()
- {
- if (_ptr)
- {
- cout << "delete:" << _ptr << endl;
- delete _ptr;
- }
- }
-
- // 像指针一样使用
- T& operator*()
- {
- return *_ptr;
- }
-
- T* operator->()
- {
- return _ptr;
- }
- private:
- T* _ptr;
- };
*
和->
运算符进行重载,使unique_ptr对象具有指针一样的行为。- namespace XM
- {
- template<class T>
- class unique_ptr
- {
- public:
- unique_ptr(T* ptr = nullptr)
- : _ptr(ptr)
- {}
-
- ~unique_ptr()
- {
- if (_ptr)
- delete _ptr;
- }
-
- T& operator*() { return *_ptr; }
- T* operator->() { return _ptr; }
-
- private:
- // C++98防拷贝的方式:只声明不实现+声明成私有
- unique_ptr(const unique_ptr
& sp); - unique_ptr& operator=(const unique_ptr
& sp); -
- // C++11防拷贝的方式:delete
- unique_ptr(const unique_ptr
& sp) = delete; - unique_ptr& operator=(const unique_ptr
& sp) = delete; -
- private:
- T* _ptr;
- };
- }
- class Date
- {
- public:
- Date() { cout << "Date()" << endl;}
- ~Date(){ cout << "~Date()" << endl;}
-
- int _year = 0;
- int _month = 0;
- int _day = 0;
- };
-
-
- int main()
- {
- // shared_ptr通过引用计数支持智能指针对象的拷贝
- std::shared_ptr
sp(new Date) ; - std::shared_ptr
copy(sp) ; -
- cout << "ref count:" << sp.use_count() << endl;
- cout << "ref count:" << copy.use_count() << endl;
- return 0;
- }
- namespace XM
- {
-
- template<class T>
- class shared_ptr
- {
- public:
- shared_ptr(T* ptr)
- :_ptr(ptr)
- , _pRefCount(new int(1))
- {}
-
- shared_ptr(const shared_ptr
& sp) - :_ptr(sp._ptr)
- , _pRefCount(sp._pRefCount)
- {
- ++(*_pRefCount);
- }
-
- shared_ptr
& operator=(const shared_ptr& sp) - {
- if (_ptr != sp._ptr)
- {
- if (--(*_pRefCount) == 0)
- {
- delete _ptr;
- delete _pRefCount;
- }
-
- _ptr = sp._ptr;
- _pRefCount = sp._pRefCount;
- ++(*_pRefCount);
- }
-
- return *this;
- }
-
- ~shared_ptr()
- {
- if (--(*_pRefCount) == 0 && _ptr)
- {
- cout << "delete:" << _ptr << endl;
- delete _ptr;
- delete _pRefCount;
-
- //_ptr = nullptr;
- //_pRefCount = nullptr;
- }
- }
-
- int use_count() const
- {
- return *_pRefCount;
- }
-
- // 像指针一样使用
- T& operator*()
- {
- return *_ptr;
- }
-
- T* operator->()
- {
- return _ptr;
- }
- private:
- T* _ptr;
- int* _pRefCount;
- };
-
- }
- #include
- #include
- #include
- #include
-
- using namespace std;
-
-
- struct Date
- {
- int _year = 0;
- int _month = 0;
- int _day = 0;
- };
-
-
- namespace XM
- {
-
- template<class T>
- class shared_ptr
- {
- public:
- shared_ptr(T* ptr)
- :_ptr(ptr)
- , _pRefCount(new int(1))
- {}
-
- shared_ptr(const shared_ptr
& sp) - :_ptr(sp._ptr)
- , _pRefCount(sp._pRefCount)
- {
- AddRef();
- }
-
- shared_ptr
& operator=(const shared_ptr& sp) - {
- if (_ptr != sp._ptr)
- {
- Release();
-
- _ptr = sp._ptr;
- _pRefCount = sp._pRefCount;
- AddRef();
-
- }
-
- return *this;
- }
-
-
-
- ~shared_ptr()
- {
- Release();
- }
-
- T* get() const
- {
- return _ptr;
- }
-
- int use_count()
- {
- return *_pRefCount;
- }
-
-
- T& operator*()
- {
- return *_ptr;
- }
-
- T* operator->()
- {
- return _ptr;
- }
-
- private:
- void Release()
- {
- if (--(*_pRefCount) == 0 && _ptr)
- {
- delete _ptr;
- delete _pRefCount;
- }
- }
-
- void AddRef() //增加计数
- {
- ++(*_pRefCount);
- }
-
- private:
- T* _ptr;
- int* _pRefCount;
- };
- }
-
-
-
- void SharePtrFunc(XM::shared_ptr
& sp, size_t n,mutex& mtx) - {
- cout << sp.get() << endl;
-
- for (size_t i = 0; i < n; ++i)
- {
- // 这里智能指针拷贝会++计数,智能指针析构会--计数,自己模拟实现是不安全的
- XM::shared_ptr
copy(sp) ; -
-
- {
- unique_lock
lk(mtx) ; - copy->_year++;
- copy->_month++;
- copy->_day++;
- }
-
- }
- }
-
- int main()
- {
- XM::shared_ptr
p(new Date) ; - cout << p.get() << endl;
- const size_t n = 10000;
- mutex mtx;
-
- thread t1(SharePtrFunc, std::ref(p), n,std::ref(mtx));
- thread t2(SharePtrFunc, std::ref(p), n,std::ref(mtx));
-
- t1.join();
- t2.join();
-
- cout << p->_year << endl;
- cout << p->_month << endl;
- cout << p->_day << endl;
-
- cout << p.use_count() << endl;
-
- return 0;
- }
①加锁保证数据安全,局部域,锁只保护这一个局部域,出了局部域就解锁了
- int main()
- {
- { //局部域
- unique_lock
lk(mtx) ; - copy->_year++;
- copy->_month++;
- copy->_day++;
- }
-
- //Do something ... 不需要加锁
- }
②shared_ptr智能指针是线程安全的吗?
③模拟线程不安全的情况尽量控制在某一个方面不安全,不要多个方面都是线程不安全的,这样线程出现错误的概率大大增加,很难模拟出想要的结果。
①要解决引用计数的线程安全问题,本质就是要让对引用计数的自增和自减操作变成一个原子操作,因此可以对引用计数的操作进行加锁保护,也可以用原子类atomic对引用计数进行封装,这里以加锁为例
- namespace XM
- {
-
- template<class T>
- class shared_ptr
- {
- public:
- shared_ptr(T* ptr)
- :_ptr(ptr)
- , _pRefCount(new int(1))
- ,_pmtx(new mutex)
- {}
-
- shared_ptr(const shared_ptr
& sp) - :_ptr(sp._ptr)
- , _pRefCount(sp._pRefCount)
- ,_pmtx(sp._pmtx)
- {
- AddRef();
- }
-
- shared_ptr
& operator=(const shared_ptr& sp) - {
- //if (this != &sp) 这样判断不太好,防止自己给自己赋值应该判断指针的值是否相同
- if (_ptr != sp._ptr)
- {
- Release();
-
- _ptr = sp._ptr;
- _pRefCount = sp._pRefCount;
- _pmtx = sp._pmtx;
- AddRef();
-
- }
-
- return *this;
- }
-
-
-
- ~shared_ptr()
- {
- Release();
- }
-
- T* get() const
- {
- return _ptr;
- }
-
- int use_count()
- {
- return *_pRefCount;
- }
-
-
- T& operator*()
- {
- return *_ptr;
- }
-
- T* operator->()
- {
- return _ptr;
- }
-
- private:
- void Release() //释放资源
- {
- _pmtx->lock();
- bool flag = false;
- if (--(*_pRefCount) == 0 && _ptr)
- {
- delete _ptr;
- delete _pRefCount;
-
- flag = true; //锁不能在这里释放,因为后面要解锁
- }
- _pmtx->unlock();
-
- if (flag == true)
- {
- delete _pmtx;
- }
- }
-
- void AddRef() //增加计数
- {
- _pmtx->lock();
-
- ++(*_pRefCount);
-
- _pmtx->unlock();
- }
-
- private:
- T* _ptr;
- int* _pRefCount;
- mutex* _pmtx;
- };
- }
②小结
- struct ListNode
- {
- int _val;
- std::shared_ptr
_next; - std::shared_ptr
_prev; -
- ~ListNode()
- {
- cout << "~ListNode()" << endl;
- }
- };
-
- int main()
- {
- std::shared_ptr
n1(new ListNode) ; - std::shared_ptr
n2(new ListNode) ; -
- cout << n1.use_count() << endl;
- cout << n2.use_count() << endl;
-
- // 循环引用
- n1->_next = n2;
- n2->_prev = n1;
-
- cout << n1.use_count() << endl;
- cout << n2.use_count() << endl;
-
- return 0;
- }
①weak_ptr介绍
②将ListNode中的next和prev成员的类型换成weak_ptr就不会导致循环引用问题了,此时当node1和node2生命周期结束时两个资源对应的引用计数就都会被减为0,进而释放这两个结点的资源。
- struct ListNode
- {
- std::weak_ptr
_next; - std::weak_ptr
_prev; - int _val;
- ~ListNode()
- {
- cout << "~ListNode()" << endl;
- }
- };
-
- int main()
- {
- std::shared_ptr
node1(new ListNode) ; - std::shared_ptr
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;
-
- return 0;
- }
- namespace cl
- {
- template<class T>
- class weak_ptr
- {
- public:
- weak_ptr()
- :_ptr(nullptr)
- {}
- weak_ptr(const shared_ptr
& sp) - :_ptr(sp.get())
- {}
- weak_ptr& operator=(const shared_ptr
& sp) - {
- _ptr = sp.get();
- return *this;
- }
- //可以像指针一样使用
- T& operator*()
- {
- return *_ptr;
- }
- T* operator->()
- {
- return _ptr;
- }
- private:
- T* _ptr; //管理的资源
- };
- }
- struct ListNode
- {
- ListNode* _next;
- ListNode* _prev;
- int _val;
- ~ListNode()
- {
- cout << "~ListNode()" << endl;
- }
- };
-
- int main()
- {
- std::shared_ptr
sp1(new ListNode[10]) ; //error - std::shared_ptr
sp2(fopen("test.cpp", "r")) ; //error -
- return 0;
- }
- template <class U, class D>
- shared_ptr (U* p, D del);
①参数
②当shared_ptr对象的生命周期结束时就会调用传入的删除器完成资源的释放,调用该删除器时会将shared_ptr管理的资源作为参数进行传入。
③因此当智能指针管理的资源不是以 new 的方式申请到的内存空间时,就需要在构造智能指针对象时传入定制的删除器
- template<class T>
- struct DelArr
- {
- void operator()(const T* ptr)
- {
- cout << "delete[]: " << ptr << endl;
- delete[] ptr;
- }
- };
-
- int main()
- {
- std::shared_ptr
sp1(new ListNode[10], DelArr()) ; //仿函数 - std::shared_ptr
sp2(fopen("test.cpp", "r"), [](FILE* ptr){ - cout << "fclose: " << ptr << endl;
- fclose(ptr);
- }); //lamba表达式
-
- return 0;
- }
- namespace XM
- {
- //默认的删除器
- template<class T>
- struct Delete
- {
- void operator()(const T* ptr)
- {
- delete ptr;
- }
- };
-
- template<class T, class D = Delete
> - class shared_ptr
- {
- private:
- void Release()
- {
- _pmtx->lock();
- bool flag = false;
- if (--(*_pRefCount) == 0 && _ptr) //将管理的资源对应的引用计数--
- {
- _del(_ptr); //使用定制删除器释放资源
- delete _pRefCount;
-
- flag = true;
- }
- _pmtx->unlock();
-
- if (flag == true)
- {
- delete _pmtx;
- }
- }
- //...
-
- public:
- shared_ptr(T* ptr, D del)
- : _ptr(ptr)
- , _pRefCount(new int(1))
- , _pmtx(new mutex)
- , _del(del)
- {}
- //...
- private:
- T* _ptr; //管理的资源
- int* _pRefCount; //管理的资源对应的引用计数
- mutex* _pmtx; //管理的资源对应的互斥锁
- D _del; //管理的资源对应的删除器
- };
- }
我们模拟出来的删除器但使用起来没有C++标准库中的那么方便
- template<class T>
- struct DelArr
- {
- void operator()(const T* ptr)
- {
- cout << "delete[]: " << ptr << endl;
- delete[] ptr;
- }
- };
-
- int main()
- {
- //仿函数示例
- XM::shared_ptr
> sp1(new ListNode[10], DelArr()); -
- //lambda示例1
- XM::shared_ptr
void(FILE*)>> sp2(fopen("test.cpp", "r"), [](FILE* ptr){ - cout << "fclose: " << ptr << endl;
- fclose(ptr);
- });
-
- //lambda示例2
- auto f = [](FILE* ptr){
- cout << "fclose: " << ptr << endl;
- fclose(ptr);
- };
- XM::shared_ptr
decltype (f)> sp3(fopen("test.cpp", "r"), f); -
- return 0;
- }