// 智能指针!
// 内存泄露:
// 堆内存泄露:
// 堆内存指的是程序执行中依据需要分配通过 malloc/calloc/new 等从堆中分配一块
// 内存,用完后必须通过调用相应的free或者delete 删掉。 假设程序的设计错误导致这部
// 分内存没有被释放,那么以后这部分内存将无法再次被使用,会产生 Heap Leak。
// 系统资源泄露:
// 指程序使用系统分配的资源,比如套接字,文件描述符,管道等没有使用对应的函数释放掉
// 导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。
// 避免内存泄漏的办法:
// 1. 采用RALL 思想或者智能指针来管理资源,(由对象的生命周期来管理内存!);
// RALL(resource acquisition is initialization) 是一种利用对象生命周期来控制程序资源的简单技术。
# if 0
在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内始终保持有效,最后在对象析构的时候释放资源。
借此,我们实际上把管理一份资源的责任交给了一个对象。
好处:
* 不需要显示的释放资源。
* 采用这种方式,对象所需的资源在其生命周期内始终保持有效。
#endif
#if 0
// 采用 RALL 的思想设计SmartPtr 类
template <class T>
class SmartPtr {
public:
SmartPtr(T* ptr = nullptr):_ptr(ptr){}
~SmartPtr() {
if (_ptr) {
delete _ptr;
}
}
private:
T* _ptr;
};
#endif
#if 0
智能指针的原理,SmartPtr 还不能将其称为只能指针,因为它还不具有指针的行为。指针可以解引用,也可以通过-> 去访问空间上的内容
因此: 要重载一线 *, -> 才能像指针一样使用。
#endif
#if 0
#include <iostream>
template <class T>
class SmartPtr {
public:
SmartPtr(T* ptr = nullptr) :_ptr(ptr) {}
T& operator*() {
return *_ptr;
}
T* operator->() {
return _ptr;
}
~SmartPtr() {
if (_ptr) {
delete _ptr;
}
}
private:
T* _ptr;
};
struct Date {
int _year;
int _month;
int _day;
};
int main() {
SmartPtr<int> sp1(new int);
*sp1 = 10;
std::cout << *sp1 << std::endl;
SmartPtr<Date> sparr(new Date);
sparr->_day = 10;
sparr->_month = 7;
sparr->_year = 2022;
std::cout << " ";
return 0;
}
#endif
#if 0
// C++ 98 提供 auto_ptr 的智能指针。(不好用,被拷贝的因该是调用了swap)
#include <iostream>
class Dat {
public:
Dat() {
std::cout << "Dat()" << std::endl;
}
~Dat() {
std::cout << "~Dat()" << std::endl;
}
int _year;
int _month;
int _day;
};
int main() {
std::auto_ptr<Dat> ap(new Dat);
std::auto_ptr<Dat> copy(ap);
// auto_ptr 的问题,当对象拷贝或者赋值后,前面的对象就悬空了.
// C++ 98 中auto_ptr 问题非常明显.
copy->_year = 2023;
ap->_year = 2022; // 悬空!== empty
return 0;
}
// 模拟实现 C++98 中的Auto_ptr
template<class T>
class AutoPtr {
public:
AutoPtr(T* ptr):_ptr(ptr){}
~AutoPtr() {
if (_ptr) {
delete _ptr;
}
}
// 拷贝构造,一旦发生拷贝,就将ap中的资源转移到当前对象中,让后让ao与其管理的资源分手,这样就解决了一块内存被多个对象使用造成的程序崩溃问题!
AutoPtr(AutoPtr<T>& ap):_ptr(ap) {
ap._ptr = nullptr;
}
AutoPtr<T>& opeartor = (AutoPtr<T>&ap){
// 防止递归赋值!
if (this != &ap) {
// 释放当前对象中的资源!
if (_ptr)
delete _ptr;
_ptr = ap._ptr;
ap._ptr = nullptr;
}
return *this;
}
T& operator*() {
return *_ptr;
}
T* operator->() {
return _ptr;
}
};
#endif
#if 0
C++ 11 做了改进,为了防止一份内存再多个对象中使用造成程序崩溃的问题,直接来一个unique_ptr;
// unique_ptr 直接简单粗暴的不允许拷贝和赋值操作,保证一个人只有一个老婆,而且不能再换老婆了!。
// 下面简单的模拟实现一下:
template<class T>
class UniquePtr {
private:
T* _ptr;
public:
UniquePtr(T* ptr) :_ptr(ptr){}
~UniquePtr() {
if (_ptr) delete _ptr;
}
T& operator*() {
return *_ptr;
}
T* operator->() {
return _ptr;
}
private:
// C++ 98 防止拷贝的方式:只声明不实现 + private
UniquePtr(const UniquePtr<T>& uq);
UniquePtr& operator=(UniquePtr<T> const&);
// C++ 11 防止拷贝的方式 == delete
UniquePtr(const UniquePtr<T>& uq) = delete;
UniquePtr& operator=(UniquePtr<T> const&) = delete;
};
#endif
#if 0
#include <iostream>
class Dat {
public:
Dat() {
std::cout << "Dat()" << std::endl;
}
~Dat() {
std::cout << "~Dat()" << std::endl;
}
int _year;
int _month;
int _day;
};
// C++ 11 提供类支持拷贝且靠谱的shared_ptr
int main() {
// shared_ptr 通过引用计数支持智能指针对象的拷贝!
std::shared_ptr<Dat> sp(new Dat);
std::shared_ptr<Dat> copy(sp);
std::cout << "ref count : " << sp.use_count() << std::endl;
std::cout << "ref count : " << copy.use_count() << std::endl;
return 0;
}
1. shared_ptr 在其内部,给每个资源都维护这一份计数器,用来记录该份资源被几个对象共享了。
2. 在对象被销毁时(调用析构函数),就说明自己不使用该资源了,对象引用计数减一。
3. 如果引用计数是 0 ,就说明自己是最后一个使用该资源的对象,必须释放该资源。
4. 如果引用计数不是0,就说明出了自己还有其他对象在使用改分资源,不能释放该资源。
#endif
#if 0
// 模拟实现一份 SharedPtr
#include <iostream>
#include <mutex>
template<class T>
class SharedPtr {
public:
SharedPtr(T* ptr = nullptr):_ptr(ptr),_pRefCount(new int(1)),_pMutex(new mutex){}
~SharedPtr() {
Release();
}
SharedPtr(const SharedPtr<T>& sp) :_ptr(ptr), _pRefCount(new int(1)), _pMutex(new mutex) {
AddRefCount();
}
SharedPtr<T>& operator=(const SharedPtr<T>& sp) {
if (this != &sp) {
// 释放管理的旧资源!
Release();
// 共享管理新对象的资源.
_ptr = sp._ptr;
_pRefCount = sp.__pRefCount;
_pMutex = sp._pMutex;
AddRefCount();
}
return *this;
}
T& operator*() {
return *_ptr;
}
T* operator->() {
return _ptr;
}
int UseCount(){ return *_pRefCount; }
T* Get() { return _ptr; }
void AddRefCount() {
_pMutex->lock();
++(*pRefCount);
_pMutex->unlock();
}
private:
void Release() {
bool = deleteflag = false;
// 引用计数 - 1
_pMutex->lock();
if (--(*pRefCount) == 0) {
delete _ptr;
delete _pRefCount;
deleteflag = true;
}
_pMutex->unlock();
if (deleteflag) {
delete _pMutex;
}
}
private:
int* _pRefCount; // 引用计数.
T* _ptr; // 指向管理资源的指针.
mutex* _pMutex; // 互斥锁
};
#endif
#if 0
智能指针(shared_ptr)的线程安全问题:
1.shared_ptr 对象通过引用计数来实现多个智能指针对象共享,所以引用计数的过程一定要是原子的。
2.shared_ptr 管理的对象存放在堆上,两个线程中同时去访问,会造线程安全问题!
智能指针(shared_ptr)的循环引用问题:
#include <iostream>
using namespace std;
struct ListNode {
int _data;
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;
return 0;
}
1. node1 和 node2 两个智能指针对象指向两个点,引用计数变成1,我们不需要手动delete
2. node1 的 _next 指向node2 ,node2 的_prev 指向 node1,引用计数变成2.
3. node1和node2 析构,引用计数减到1,但是_next 还指向下一个节点._prev还指向上一个节点。
也就是说_next析构了node2就释放了。
也就是说_prev 析构了 node1 就释放了。
4. 但是_next 属于node成员变量,node1释放了_next才会析构,而node1由_prev管理,_prev属于node2成员,
所以说真就叫循环引用.
注意: 解决循环引用的方案,在引用计数的场景下,将_prev,_next, 改成weak_ptr就行了!
// 原理上就是,node1->_next = node2; 和 node2 -> _prev = node1 ; 是weak_ptr 的_next 和 _prev 不会增加node1 和node2的引用计数。
#endif
#if 0
如果不是new 出来的对象如何通过智能指针管理呢? 其实shared_ptr 设计了一个删除器来解决问题!
// 仿函数的删除器
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 << "free : " << 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), deleteArrayRunc);
return 0;
}
#endif
#if 0
C++11 和 boost中智能指针的关系。
1. C++ 98 中产生了第一个智能指针 auto_ptr;
2. C++ boost 给出了更实用的scoped_ptr和 shared_ptr 和 weak_ptr;
3. C++11 TR1 ,引入了shared_ptr等。不过注意的是TR1并不是标准版的.
4. C++11 引入了unique_ptr和shared_ptr 和 weak_ptr. 需要注意的是unique_ptr 对应boost的scoped_ptr.
这些智能指针的实现原理都是参考boost中实现的.
#endif
#if 0
RALL 的思除了设计只能指针,还可以用来设计守卫锁,防止异常安全导致死锁问题!
#include <thread>
#include <mutex>
// C++ 11 库中有一个 lock_guard 下面模仿一下!
template<class Mutex>
class LockGuard {
LockGuard(Mutex& mtx):_mutex(mtx) {
_mutex.lock();
}
~LockGuard() {
_mutex.unlock();
}
private:
Mutex& _mutex; // 注意这里必须使用引用,否则搜就不是一个互斥量对象。
};
mutex mtx;
static int n = 0;
void Func() {
for (size_t i = 0; i < 10000; i++) {
LockGuard<mutex> lock(mtx);
++n;
}
}
int main() {
int begin = clock();
thread t1(Func);
thread t2(Func);
t1.join();
t2.join();
int end = clock();
count << n << endl;
count << "cost time: " << end - begin << endl;
return 0;
}
#endif
自学C++ day13 智能指针
最新推荐文章于 2024-08-12 16:55:46 发布