智能指针是防止在抛出异常后,直接跳到catch(),导致内存泄漏或在多线程的情况下发生死锁导致程序无法进行。
比如:
template<class T>
class SmartPtr
{
public:
SmartPtr(T* ptr)
:_ptr(ptr)
{ }
~SmartPtr()
{
if (_ptr)
{
cout << "~SmartPtr():" << endl;
//delete _ptr;
//_ptr = nullptr;
}
}
private:
T* _ptr;
};
void fun1()
{
int a, b;
cin >> a >> b;
int* p = new int(a);
SmartPtr<int> sp(p);
if (b == 0)
{
throw "b==0?";
}
int c = a / b;
}
void test1()
{
try
{
fun1();
}
catch (const char* errsmg)
{
cout << errsmg << endl;
}
}
在跳出fun1()函数是就会自动调用sp的析构函数。
但会出现多个指针管理同一资源的情况,导致重复释放问题,一下几种解决方式。
1. auto_ptr
管理权转移,原来的指针置空。
template<class T>
class AutoPtr
{ public:
AutoPtr(T* ptr)
:_ptr(ptr)
{ }
~AutoPtr()
{
if (_ptr)
{
cout << "~AutoPtr():" << endl;
delete _ptr;
_ptr = nullptr;
}
}
AutoPtr(AutoPtr<T>& at)
{
_ptr = at._ptr();
at._ptr = nullptr;
}
void operator=(AutoPtr<T>& at)
{
delete _ptr;
_ptr = nullptr;
_ptr = at._ptr;
at._ptr = nullptr;
}
private:
T* _ptr;
};
这种方式使原来是指针不能再使用了,悬空,不建议使用。
2.unique_ptr
禁止拷贝,一个资源只能由于一个智能指针管理。
template<class T>
class uniqueptr
{
public:
uniqueptr(T* ptr)
:_ptr(ptr)
{
}
~uniqueptr()
{
if (_ptr)
{
cout << "~uniqueptr():" << endl;
delete _ptr;
_ptr = nullptr;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
uniqueptr(const uniqueptr<T>& uq) = delete;
void operator=(const uniqueptr<T>& uq) = delete;
private:
T* _ptr;
};
3.shared_ptr
共享指针,一块资源可以有多个智能指针管理,但为了只允许释放一次(最后一个智能指针释放),添加了计数器,当减减之后为计数为0就释放。
template<class T>
class shared_ptr
{
public:
shared_ptr(T* ptr)
:_ptr(ptr)
,_pcount(new int(1))
{
cout << "构造函数:" << *_pcount << endl;
}
~shared_ptr<T>()
{
if (--(*_pcount) <= 0 && _ptr)
{
cout << "~shared_ptr():" << *_pcount + 1 << "-->" << *_pcount << endl;
delete _ptr;
_ptr = nullptr;
delete _pcount;
_pcount = nullptr;
}
else
{
cout << "没有析构:" << (*_pcount) + 1 << "-->" << *_pcount << endl;
}
}
shared_ptr<T>(const shared_ptr<T>& sp)
{
_ptr = sp._ptr;
_pcount = sp._pcount;
(*_pcount)++;
cout << "拷贝构造shared_ptr():" << (*_pcount) -1<< "-->" << *_pcount << endl;
}
void operator=(const shared_ptr<T>& sp)
{
delete _ptr;
_ptr = nullptr;
_ptr = sp._ptr;
_pcount = sp._pcount;
(*_pcount)++;
cout << "赋值重载void operator=" << (*_pcount)-1 << "-->" << *_pcount << endl;
}
int& Getcount()
{
return *_pcount;
}
private:
T* _ptr;
int* _pcount; // 用于统计管理该指针对于内存的对象的个数,当为0时,该空间可以delete
};
void test4()
{
GuYu::shared_ptr<int> sp(new int(1));
GuYu::shared_ptr<int> sp1 = sp;
}
结果:
构造函数:1
拷贝构造shared_ptr():1-->2
没有析构:2-->1
~shared_ptr():1-->0
只在最后一个管理者析构时释放。
有了计数就要考虑线程安全问题,因为什么时候析构依赖于*_pcount,当线程冲突导致*_pcount少于实际存在的智能指针的数量时,导致重复释放空间。
需要加一把锁,并且每个管理的智能指针用的是同一把锁。
锁是不支持拷贝构造的,他们的拷贝构造函数都被delete了。
线程安全:
template<class T>
class shared_ptr2
{
public:
shared_ptr2(T* ptr=nullptr)
:_ptr(ptr)
,_pcount(new int(1))
,_mtx(new mutex)
{
cout << "构造函数:" << *_pcount << endl;
}
~shared_ptr2<T>()
{
bool flag = false;
_mtx->lock();
if (--(*_pcount) == 0 && _ptr)
{
cout << "~shared_ptr2():" << *_pcount + 1 << "-->" << *_pcount << endl;
delete _ptr;
_ptr = nullptr;
delete _pcount;
_pcount = nullptr;
flag = true;
}
else
{
cout << "没有析构:" << (*_pcount) + 1 << "-->" << *_pcount << endl;
}
_mtx->unlock();
if (flag)
{
delete _mtx;
_mtx = nullptr;
}
}
shared_ptr2<T>(const shared_ptr2<T>& sp)
{
sp._mtx->lock();
_ptr = sp._ptr;
_pcount = sp._pcount;
_mtx = sp._mtx;
(*_pcount)++;
cout << "拷贝构造shared_ptr2():" << (*_pcount) -1<< "-->" << *_pcount << endl;
sp._mtx->unlock();
}
void operator=(const shared_ptr2<T>& sp)
{
sp._mtx->lock();
delete _ptr;
delete _mtx;
_ptr = nullptr;
_mtx = nullptr;
_mtx = sp._mtx;
_ptr = sp._ptr;
_pcount = sp._pcount;
(*_pcount)++;
cout << "赋值重载void operator=" << (*_pcount)-1 << "-->" << *_pcount << endl;
sp._mtx->unlock();
}
T& operator*()
{return *_ptr;}
T* operator->()
{return _ptr;}
T& operator[](size_t pos)
{return _ptr[pos];}
T* Getptr()
{return _ptr;}
int& Getcount()
{return *_pcount;}
private:
T* _ptr;
int* _pcount; // 用于统计管理该指针对于内存的对象的个数,当为0时,该空间可以delete
mutex* _mtx; //用同一把锁
};
循环引用问题
struct Node
{
Node()
:val(0)
,_next(nullptr)
,_prev(nullptr)
{ }
~Node()
{
cout << "~Node()" << endl;
}
shared_ptr2<Node> _prev;
shared_ptr2<Node> _next;
//Node* _prev;
//Node* _next;
int val;
};
void test6()
{
shared_ptr2<Node> sp1(new Node);
shared_ptr2<Node> sp2(new Node);
sp1.Getptr()->_next = sp2;
sp2.Getptr()->_prev = sp1; // 没有被析构释放
}
结果:
构造函数:1 //_prev
构造函数:1 //_next
构造函数:1 //_sp1
构造函数:1 //_prev
构造函数:1 //_next
构造函数:1 //_sp2
赋值重载void operator=1-->2
赋值重载void operator=1-->2
没有析构:2-->1
没有析构:2-->1
可见,并没有成功释放空间,sp1等待着sp2的_prev释放,sp1走了,同时带走了sp1的_next,也就是sp2的一个_pcount,但sp2的_pcount没有减减,导致sp2走了,也没有释放空间。
那么对于_prev和_next就不参与_pcount的加减。
template<class T>
class weak_ptr
{
public:
weak_ptr() = default; // 显示默认构造函数
weak_ptr(const shared_ptr2<T>& sp)
{
cout << "weak_ptr(const shared_ptr2<T>& sp)拷贝:" << endl;
_ptr = sp._ptr;
}
weak_ptr<T>& operator=(shared_ptr2<T>& sp)
{
cout << "weak_ptr<T>& operator=(const shared_ptr2<T>& sp)赋值重载:" << endl;
_ptr = sp.Getptr();
return *this;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
T& operator[](size_t pos)
{
return _ptr[pos];
}
private:
T* _ptr;
};
}
struct Node
{
Node()
:val(0)
{ }
GuYu::weak_ptr<Node> _prev; // 调用默认构造函数
GuYu::weak_ptr<Node> _next;
int val;
};
shared_ptr的delete使用
int i = 0;
class A
{
public:
A() = default;
~A()
{
cout <<i++<<" : "<< "~A()" << " ";
}
A(int n)
{
;
}
private:
};
template<class A>
class DeleteArr
{
public:
void operator()(A* arr)
{
cout << "delete [] arr" << endl;
delete[]arr;
cout << endl;
cout << "over delete [] arr" << endl;
}
};
template<class A>
class Free
{
public:
void operator()(A* arr)
{
cout << "free [] arr" << endl;
free(arr);
cout << "over free [] arr" << endl;
}
};
template<class A>
class Closefile
{
public:
void operator()(A* pf)
{
cout << "Closefile [] arr" << endl;
fclose(pf);
cout << "over Closefile [] arr" << endl;
}
};
void test7()
{
std::shared_ptr<A> sp1(new A);
//std::shared_ptr<A> sp2(new A[10]); // 会发生 异常
std::shared_ptr<A> sp2(new A[10], DeleteArr<A>()); // C++11
std::shared_ptr<A> sp3(new A[10], [](A* p) {cout << endl;; cout << "lambda" << endl;; delete[]p; cout << endl; }); // C++11
std::shared_ptr<A[]> sp4(new A[10]); // C++17
//std::shared_ptr<A>sp5((A*)malloc(sizeof(A) * 10)); // 内存泄漏,没有释放
std::shared_ptr<A>sp5((A*)malloc(sizeof(A) * 10),Free<A>());
FILE* pf;
errno_t err = fopen_s(&pf, "test.txt", "w");
std::shared_ptr<FILE> sp6(pf,Closefile<FILE>());
}
结果:
Closefile [] arr
over Closefile [] arr
free [] arr
over free [] arr
0 : ~A() 1 : ~A() 2 : ~A() 3 : ~A() 4 : ~A() 5 : ~A() 6 : ~A() 7 : ~A() 8 : ~A() 9 : ~A()
lambda
10 : ~A() 11 : ~A() 12 : ~A() 13 : ~A() 14 : ~A() 15 : ~A() 16 : ~A() 17 : ~A() 18 : ~A() 19 : ~A()
delete [] arr
20 : ~A() 21 : ~A() 22 : ~A() 23 : ~A() 24 : ~A() 25 : ~A() 26 : ~A() 27 : ~A() 28 : ~A() 29 : ~A()
over delete [] arr
30 : ~A()
只能在堆上创建对象
class OnlyHeap // 禁止调用默认构造函数,赋值重载,拷贝函数
{
public:
static OnlyHeap* Getobj() // 静态变量不需要实例化对象就可以使用
{
return new OnlyHeap; // 调用默认构造函数
}
OnlyHeap(const OnlyHeap& hp) = delete;
void operator=(const OnlyHeap& hp) = delete;
private:
OnlyHeap() {};
};
void test9()
{
OnlyHeap* hp = OnlyHeap::Getobj();
// OnlyHeap hp2(*hp); 已被删除的函数
}
只能在栈上创建对象
class OnlyStack // 只要禁止new调用默认构造函数
{
public:
static OnlyStack Getobj()
{
return OnlyStack();
}
// void* operator new(size_t size) = delete; // 如果只写这种方式,会有缺陷,当创建的对象是静态的 --> static OnlyStack sk= new OnlyStack ; 就不会受限制
private:
OnlyStack() {};
};
void test10()
{
OnlyStack sk = OnlyStack::Getobj();
// OnlyStack* sk2 = new OnlyStack; // 默认构造函数无法访问
}