C++智能指针
智能指针介绍
- 内存泄漏
-
堆内存泄漏:在堆上申请资源,在结束使用的时候没有释放归还给OS,导致该块内存不会被再次使用
-
资源泄露:系统资源如socket、文件描述符等,这些在系统中是有限制的,如果创建了不归还就会耗尽资源,导致其他程序不可用
- 智能指针
-
C#和Java中都有自动垃圾回收机制,GC可以管理分配的堆内存,在对象失去引用时自动回收,因此,在C#和Java中内存管理不是问题
-
C++语言没有垃圾回收机制,必须自己去释放动态分配的堆内存
-
解决这个问题最有效的方法是使用智能指针智能指针,在智能指针对象中有一个指针,此指针存储的是动态创建对象的地址,用于生存期控制,能够确保智能指针对象离开所在作用域时,自动正确地销毁动态创建的对象,防止内存泄漏
- RAII
-
RAII:Resource Acquisition Is Initialiaztion,资源获取即初始化,使用局部对象来管理资源的技术
-
RAII充分利用了C++语言局部对象自动销毁的特性来控制资源的生命周期,资源主要是指操作系统中有限的如内存heap、网络套接字、互斥量、文件句柄等,局部对象存储在栈中的对象,其生命周期由操作系统管理
-
RAII过程总结:
-
设计一个类封装资源
-
在构造函数中初始化
-
在析构函数中执行销毁操作
-
使用时定义一个该类的局部对象
-
实现一个简单的智能指针
- 构造
template<typename T>
class mySmartPoint{
public:
mySmartPoint(T *ptr = nullptr)
: _ptr(ptr) {}
~mySmartPoint() { delete _ptr; }
private:
T *_ptr;
};
int main(){
//栈上的对象出作用域自动析构对象
mySmartPoint<int> point1(new int);
return 0;
}
- 添加指针操作符重载函数
template<typename T>
class mySmartPoint{
public:
mySmartPoint(T *ptr = nullptr)
: _ptr(ptr) {}
~mySmartPoint() { delete _ptr; }
T& operator*() { return *_ptr;}
T* operator->() { return _ptr;}
private:
T *_ptr;
};
不带引用计数的智能指针
- 不带引用计数的智能指针推荐使用unique_ptr
//unique_ptr(unique_ptr<T> &&src)
//unique_ptr<T>& operator=(unique_ptr<T> &&src)
unique_ptr<int> p1(new int);
unique_ptr<int> p2(std::move(p1));//std::move得到当前变量的右值类型
带引用计数的智能指针
-
带引用计数的智能指针share_ptr和weak_ptr
-
多个智能指针可以管理同一个资源
-
给每个对象资源匹配一个引用计数,有资源的时候引用计数加1,不使用资源的时候引用计数减1,引用计数为0时释放资源
template<typename T>
class RefCnt{
public:
RefCnt(T *ptr = nullptr) : _ptr(ptr){
if(_ptr != nullptr) { _cnt = 1;}
}
~RefCnt(){ delete _ptr; _ptr = nullptr; }
void addRefCnt(){
_cnt++;//添加引用计数
}
int delRefCnt(){
return --_cnt;//减少引用计数
}
private:
T* _ptr;
int _cnt;
};
template<typename T>
class mySmartPoint{
public:
mySmartPoint(T *ptr = nullptr) : _ptr(ptr) {
_pRefCnt = new RefCnt<T>(_ptr);
}
~mySmartPoint(){
if(0 == _pRefCnt->delRefCnt()){
delete _ptr;
_ptr = nullptr;
}
}
T& operator*() { return *_ptr;}
T* operator->() { return _ptr;}
mySmartPoint(const mySmartPoint<T> &src)
: _ptr(src._ptr), _pRefCnt(src._pRefCnt){
if(_ptr != nullptr)
_pRefCnt->addRefCnt();
}
mySmartPoint<T>& operator=(const mySmartPoint<T> &src){
if(this == &src)
return *this;
if(0 == _pRefCnt->delRefCnt())
delete _ptr;
_ptr = src._ptr;
_pRefCnt = src._pRefCnt;
_pRefCnt->addRefCnt();
return *this;
};
private:
T *_ptr;//指向资源的指针
RefCnt<T> *_pRefCnt;//指向该资源引用计数对象的指针
};
int main(){
mySmartPoint<int> p1(new int);
mySmartPoint<int> p2(p1);
mySmartPoint<int> p3;
p3 = p2;
}
shared_ptr的交叉引用问题
-
shared_ptr强智能指针,可以改变资源的引用计数
-
weak_ptr弱智能指针,不会改变资源的引用计数,观察资源,没有提供*和->运算符重载函数
-
强智能指针的交叉引用问题:
class B;
class A{
public:
A() {cout << "A()" << endl;}
~A(){cout << "~A()" << endl;}
shared_ptr<B> _ptrB;
};
class B{
public:
B() {cout << "B()" << endl;}
~B(){cout << "~B()" << endl;}
shared_ptr<A> _ptrA;
};
int main(){
shared_ptr<A> pA(new A());
shared_ptr<B> pB(new B());
pA->_ptrB = pB;
pB->_ptrA = pA;
cout << pA.use_count() << endl;//2
cout << pB.use_count() << endl;//2
return 0;
}
- 解决方法:定义对象的时候用强智能指针;引用对象的地方用弱智能指针
class B;
class A{
public:
A() {cout << "A()" << endl;}
~A(){cout << "~A()" << endl;}
weak_ptr<B> _ptrB;
};
class B{
public:
B() {cout << "B()" << endl;}
~B(){cout << "~B()" << endl;}
weak_ptr<A> _ptrA;
};
int main(){
shared_ptr<A> pA(new A());
shared_ptr<B> pB(new B());
pA->_ptrB = pB;
pB->_ptrA = pA;
cout << pA.use_count() << endl;//1
cout << pB.use_count() << endl;//1
return 0;
}
- 应用:多线程访问共享对象的线程安全问题
class A{
public:
A() {cout << "A()" << endl;}
~A(){cout << "~A()" << endl;}
void showA(){ cout << "showA()" << endl;}
//weak_ptr<B> _ptrB;
};
//子线程
void handle01(weak_ptr<A> pw){
//std::this_thread::sleep_for(std::chrono::seconds(2));
//q访问A对象的时候,需要侦测A对象是否存活
shared_ptr<A> sp = pw.lock();
if( sp != nullptr){
sp->showA();
}
else{
cout << "A对象被析构了不能访问" << endl;
}
}
//main线程
int main(){
{
shared_ptr<A> p(new A());
thread t1(handle01, weak_ptr<A> (p));
t1.detach();
std::this_thread::sleep_for(std::chrono::seconds(2));
}
//阻塞等待子线程结束
std::this_thread::sleep_for(std::chrono::seconds(20));
return 0;
}
智能指针的删除器
-
unique_ptr的析构函数是一个函数对象的调用deletor(ptr)
-
默认的deletor定义
template<typename T>
class Deletor{
public:
void operator()(T *ptr){
delete ptr;
}
};
- 自定义删除器,先定义一个类型,该方法不推荐
template<typename T>
class myDeletor{
public:
void operator()(T *ptr)const{
cout << "call myDeletor.operator()" << endl;
delete[]ptr;
}
};
template<typename T>
class myFileDeletor{
public:
void operator()(T *ptr)const{
cout << "call myFileDeletor.operator()" << endl;
fclose(ptr);
}
};
int main(){
unique_ptr<int, myDeletor<int>> p1(new int[100]);
unique_ptr<FILE, myFileDeletor<FILE>> p2(fopen("data.txt", "w"));
//先构造的后析构
return 0;
}
- 自定义删除器,lambda表达式=》函数对象 function
int main(){
unique_ptr<int, function<void (int*)>> p1(new int[100],
[](int *ptr)->void
{
cout << "call lambda release new int[100]" << endl;
delete[]ptr;
});
unique_ptr<FILE, function<void (FILE*)>> p2(fopen("data.txt", "w"),
[](FILE *ptr)->void
{
cout << "call lambda release new fopen()" << endl;
fclose(ptr);
});
return 0;
}