RALL资源获取初始化,删除器
2024-10-19 12:24:49
body, table{font-family: 微软雅黑; font-size: 10pt}
table{border-collapse: collapse; border: solid gray; border-width: 2px 0 2px 0;}
th{border: 1px solid gray; padding: 4px; background-color: #DDD;}
td{border: 1px solid gray; padding: 4px;}
tr:nth-child(2n){background-color: #f8f8f8;}
资源管理--RAII:
1、RAII(Resource Acquisition Is Initialization)是一种由 C++创造者 Bjarne Stroustrup 提出的, 利用对象生命周期管理程序资源(包括内存、文件句柄、锁等)的技术。
2、使用 RAII 时,一般在资源获得的同时构造对象, 在对象生存期间,资源一直保持有效;对象析构时,资源被释放。
关键:要保证资源的释放顺序与获取顺序严格相反
3、RAII类的常见特征
1、在构造时初始化资源, 或托管已构造的资源
2、析构时释放资源
3、一般不允许复制或赋值(对象语义)
4、提供若干访问资源的方法
|
4、RAII的本质是用栈对象来管理资源,因为栈对象在离开作用域时,会自动调用析构函数
#include <iostream>
#include <stdio.h>
#include <string>
using namespace std;
class SafeFile
{
public:
SafeFile(const string & filename)
:_fp(fopen(filename.c_str(),"w+"))
{
cout<<"fopen(_fp)"<<endl;
if(NULL == _fp)
{
cout<< "FILE open error"<<endl;
}
}
~SafeFile()
{
if(_fp != NULL)
{
fclose(_fp);
cout<< "fclose(_fp) "<<endl;
}
}
|
void write(const char * str)
{
cout<<"write(const char *)"<<endl;
if(fputs(str,_fp)==EOF)
{
cout<< "write error!" <<endl;
}
}
private:
FILE * _fp;
SafeFile(const SafeFile & rhs);
SafeFile & operator = (const SafeFile & rhs);
};
int main()
{
SafeFile sf("test.txt");
sf.write("hello,world!\n");
return 0;
}
|
/**
**利用一个对象在离开个域中会调用析构函数的特性,
**在构造函数中完成初始化,在析构函数中完成清理工作,将需要
**操作和保护的指针作为成员变量放入RAII中。
*/
#include<iostream>
using namespace std;
template<typename T>
class RALL
{
public:
RALL(T* p):_p(p) { cout<<"托管资源"<<endl; }
~RALL() // 托管资源,可能多个指针指向同一块内存区域,if判断不能发挥作用
{
if(NULL!=_p)
{
delete _p;
_p=NULL;
} cout<<"释放资源"<<endl;
}
// 资源指针,被托管的都是资源的地址,
// 所以要重载这些运算符,能够获取
// 资源的地址
T* get()const;
T& operator*()const; //目的就是返回托管的指针指向的值
T* operator->()const; // 似乎没怎么用到这个
void reset(T* new_p); // 更改托管资源地址
void swap(RALL<T> &rhs); // 交换地址
private:
RALL(const RALL<T>&); //禁止调用拷贝构造函数
RALL<T>& operator=(const RALL<T>&); // 禁止调用赋值构造函数
private:
T* _p; // _p指向被托管的资源
};
template<typename T>
T* RALL<T>::get()const
{
return _p;
}
template<typename T>
T& RALL<T>::operator*()const
{
return *_p;
}
template<typename T>
T* RALL<T>::operator->()const
{
return _p;
}
template<typename T>
void RALL<T>::reset(T* new_p)
{
delete _p;
_p = new_p;
}
template<typename T>
void RALL<T>::swap(RALL<T> &rhs)
{
std::swap(_p,rhs._p);
}
|
#include<iostream>
#include"RALL.h"
using namespace std;
class resource
{
public:
resource(){cout<<"resource::resouce()"<<endl;}
~resource(){cout<<"resource::~resource()"<<endl;}
private:
};
class test
{
public:
test():_p(new resource){ cout<<"test::test()"<<endl; }
~test(){ cout<<"test::~test()"<<endl; }
//拷贝构造函数
//RALL类重载了*运算符,这里new的同时
//直接用rhs的值来初始化新开辟的空间
test(const test& rhs):_p(new resource(*rhs._p))
{ cout<<"test::test(const test&)"<<endl; }
// test& operator=(const test& rhs)
// {
// cout<<"test::operator=(const test&)"<<endl;
// if(this==&rhs){return *this;}
// //delete this->_p.get();
// //*_p = *(rhs._p);
// _p.reset((rhs._p).get()); // 函数里面已经写了释放
// return *this;
// }
private:
test& operator=(const test& rhs); // 赋值构造函数造成多个指针指向一块区域
// dubble free
private:
RALL<resource> _p;
};
int main()
{
test em;
cout<<"--------------------------"<<endl;
test em2;
cout<<"**************************"<<endl;
//em2 = em; // 这里赋值了会导致em2和em释放,段错误
//后面我在RALL类里面的析构函数做了特殊处理,指针为NULL才delete
//结果没考虑多个指针指向一块区域
cout<<"准备开始释放资源:"<<endl;
return 0;
}
|
资源管理--智能指针:
——是存储指向动态分配(堆)对象指针的类
——在面对异常的时候格外有用,因为他们能够确保正确的销毁动态分配的对象
——RAII类模拟智能指针
C++11提供了以下几种智能指针,位于头文件 #include<memory> ,它们都是类模板
std::auto_ptr(复制/赋值) 现在已淘汰
std::unique_ptr c++11
std::shared_ptr c++11
std::weak_ptr c++11
g++ -std=c++11 xx.cc
|
1、std::auto_ptr在构造时获取对某个对象的所有权(ownership),在析构时释放该对象
2、std::auto_ptr要求其对“裸”指针的完全占有性---->
——在拷贝构造或赋值操作时,会发生所有权的转移 //两次使用auto_ptr,第一次的会转移,转移了之后就失效了,其后不能使用,只能最终获得转移权的智能指针使用
3、本身存在缺陷
#include <iostream>
#include <memory>
using namespace std;
int main()
{
double * pd = new double(88.99);
auto_ptr<double> app(pd);
cout<<"*app = "<<*app<<endl;
cout<<"app.get()= "<<app.get()<<endl;
cout<<"pd = "<<pd<<endl;
int * pi = new int(5);
auto_ptr<int> api(pi);
auto_ptr<int> api2(api); //这里实际上是拷贝
//表达的是值语义,但是实现有缺陷,在底层已经发生了所有权的转移
cout<<"*api2 = "<<*api2<<endl;
cout<<"*api = "<<*api<<endl; //段错误,api已经不能在使用了
return 0;
}
|
4、std::unique_ptr是一个独享所有权的智能指针,它提供了一种严格语义上的所有权,包括:
——拥有它所指向的对象
——无法进行复制、赋值操作
——保存指向某个对象的指针,当它本身被删除释放的时候,会使用给定的删除器释放它指向的对象
——具有移动(std::move)语义(和前面的把左值改右值一样),可做为容器元素
1.无法进行复制、赋值操作
std::unique_ptr<int> ap(new int(88 );
std::unique_ptr<int> one (ap) ; // 会出错
std::unique_ptr<int> two = one; //会出错
3.可做为容器元素
unique_ptr<int> sp(new int(88));
vector<unique_ptr<int> > vec;
vec.push_back(std::move(sp));
//vec.push_back( sp ); 这样不行,会报错的.
//cout<<*sp<<endl;但这个也同样出错,说明sp添加到容器中之后,它自身报废了.
|
2.可以进行移动构造和移动赋值操作
unique_ptr<int> GetVal( ){
unique_ptr<int> up(new int(88 );
return up;
}
unique_ptr<int> uPtr = GetVal(); //ok
实际上上面的的操作有点类似于如下操作
unique_ptr<int> up(new int(88 );
unique_ptr<int> uPtr2 = std::move(up) ; //这里是显式的所有权转移. 把up所指的内存转给uPtr2了,而up不再拥有该内存.
|
#include <iostream>
#include <memory>
#include <vector>
using namespace std;
unique_ptr<int> getValue()
{
unique_ptr<int> upi(new int (88));
return upi; //最后获得的是一个右值
}
int main()
{
//unique_ptr无法进行复制或赋值,表达的是对象语义
unique_ptr<int> one(new int(1));
//无法进行复制、赋值操作
// unique_ptr<int> two(one); //错
// unique_ptr<int> three=one; //错
|
//可以进行移动构造和移动赋值操作
// 调用的是移动构造函数
unique_ptr<int> tmp = getValue();
unique_ptr<int> up(new int(88));
unique_ptr<int> up2 = move(up);
//这里把显示的左值所有权转移,把up所指的内存转移给up2,而up不再拥有该内存
// cout<<" *up = "<<*up<<endl; //段错误,move之后不能再使用
//可做为容器元素
unique_ptr<int> sp(new int(33));
vector<unique_ptr<int> > vec;
vec.push_back(move(sp)); //必须转换成右值
// cout<<*sp<<endl; //段错误,在添加到容器的过程中转换成右值,自身不再拥有内存
return 0;
}
|
5、std::shared_ptr是一个引用计数智能指针,用于共享对象的所有权
——引进了一个计数器shared_count,用来表示当前有多少个智能指针对象共享指针指向的内存块
——析构函数中不是直接释放指针对应的内存块,如果shared_count大于0则不释放内存只是将引用计数减1,只有计数等于0时释放内存
——复制构造与赋值操作符只是提供一般意义上的复制功能,并且将引用计数加1.
——问题:循环引用(最终导致内存泄露)
#include<iostream>
#include<memory>
using namespace std;
int main()
{
shared_ptr<int> sp(new int(1));
cout<<"*sp="<<*sp<<" sp.use_count="<<sp.use_count()<<endl;
shared_ptr<int> sp2 = sp; // 赋值,只是引用计数增加1
cout<<"*sp="<<*sp<<" sp.use_count="<<sp.use_count()<<endl;
cout<<"*sp2="<<*sp2<<" sp2.use_count="<<sp2.use_count()<<endl;
}
|
6、std::shared_ptr是强引用智能指针
强引用,只要有一个引用存在,对象就不能被释放
#include <iostream>
#include <memory>
using namespace std;
class Parent; //前向声明
class Child
{
public:
Child() { cout<< "Child()" <<endl; }
~Child() { cout<< "~Child()" <<endl; }
shared_ptr<Parent> _parentPtr;
};
class Parent
{
public:
Parent() { cout<< "Parent()" <<endl; }
~Parent() { cout<< "~Parent()" <<endl; }
shared_ptr<Child> _childPtr;
};
|
int main()
{//问题是:循环引用,发生内存泄露
shared_ptr<Parent> parentPtr(new Parent);
shared_ptr<Child> childPtr(new Child);
cout<< "parent' use_count()= "<<parentPtr.use_count()<<endl;
cout<< "child' use_count()= "<<childPtr.use_count()<<endl;
parentPtr->_childPtr = childPtr;
childPtr->_parentPtr = parentPtr;
cout<< "parent' use_count()= "<<parentPtr.use_count()<<endl;
cout<< "child' use_count()= "<<childPtr.use_count()<<endl;
return 0;
}
|
7、std::weak_ptr 是弱引用智能指针
——强引用,只要有一个引用存在,对象就不能被释放
——弱引用,并不增加对象的引用计数,但它知道对象是否存在。如果存在,提升为shared_ptr成功;否则,提升失败
——通过weak_ptr访问对象的成员的时候,要提升为shared_ptr
shared_ptr的误用
class std::enable_shared_from_this
方法shared_from_this()
删除器
|
#include <iostream>
#include <memory>
using namespace std;
class Parent; //前向声明
class Child
{
public:
Child() { cout<< "Child()" <<endl; }
~Child() { cout<< "~Child()" <<endl; }
weak_ptr<Parent> _parentPtr; //弱引用
};
class Parent
{
public:
Parent() { cout<< "Parent()" <<endl; }
~Parent() { cout<< "~Parent()" <<endl; }
shared_ptr<Child> _childPtr;
//weak_ptr<Child> _parentPtr;
};
int main()
{//问题是:循环引用,发生内存泄露;使用weak_ptr能够打破循环引用
shared_ptr<Parent> parentPtr(new Parent);
shared_ptr<Child> childPtr(new Child);
cout<< "parent' use_count()= "<<parentPtr.use_count()<<endl;
cout<< "child' use_count()= "<<childPtr.use_count()<<endl;
parentPtr->_childPtr = childPtr;
childPtr->_parentPtr = parentPtr;
cout<< "parent' use_count()= "<<parentPtr.use_count()<<endl;
cout<< "child' use_count()= "<<childPtr.use_count()<<endl;
return 0;
}
|
|
#include<iostream>
#include<memory>
using namespace std;
class X
{
public:
X(){ cout<<"X()"<<endl; }
~X(){ cout<<"~X()"<<endl; }
void fun(){ cout<<"X::fun()"<<endl; }
};
int main()
{
weak_ptr<X> wp;
{
cout<<"wp use count="<<wp.use_count()<<endl; // weak_ptr没有get成员
cout<<endl;
shared_ptr<X> sp(new X);
cout<<"addr ap="<<sp.get()<<" sp use count="<<sp.use_count()<<endl;
cout<<endl;
wp = sp; // 并没有增加引用计数
cout<<"addr ap="<<sp.get()<<" sp use count="<<sp.use_count()<<endl;
cout<<"wp use count="<<wp.use_count()<<endl;
cout<<endl;
shared_ptr<X> sp2 = wp.lock(); // 弱引用要提升为强引用才能访问托管对象
cout<<"addr ap="<<sp.get()<<" sp use count="<<sp.use_count()<<endl;
cout<<"wp use count="<<wp.use_count()<<endl;
cout<<"addr sp2="<<sp2.get()<<" sp2 use count="<<sp2.use_count()<<endl;
if(!sp2){ cout<<"object is destroyed!"<<endl; }
else
{ // 引用计数+1
sp2->fun();
cout<<"weak_ptr lock 成功"<<endl;
}
} // 离开作用域,sp、sp2被释放 weak_ptr知道自己托管的对象是否释放了
|
cout<<endl;
shared_ptr<X> sp3 = wp.lock(); // new X的对象已经释放了,提升失败
if(!sp3){ cout<<"object is destroyed!"<<endl; }
else
{
sp3->fun();
cout<<"weak_ptr lock 成功"<<endl;
}
cout<<"wp use count="<<wp.use_count()<<endl;
cout<<"addr sp3="<<sp3.get()<<" sp3 use count="<<sp3.use_count()<<endl;
return 0;
}
|
8、shared_ptr的误用
——class std::enable_shared_from_this
——方法 shared_from_this()
——删除器
class A:public enable_share_from_this<A>
使用场合:当类A被share_ptr管理,且在类A的成员函数里需要把当前类对象作为参数传给其他函数时,就需要传递一个指向自身的share_ptr。
我们就使类A继承enable_share_from_this,然后通过其成员函数share_from_this()返回当指向自身的share_ptr。
以上有2个疑惑:
1.把当前类对象作为参数传给其他函数时,为什么要传递share_ptr呢?直接传递this指针不可以吗?
一个裸指针传递给调用者,谁也不知道调用者会干什么?假如调用者delete了该对象,而share_ptr此时还指向该对象。
2.这样传递share_ptr可以吗?share_ptr<this>
这样会造成2个非共享的share_ptr指向一个对象(拷贝)(一个指针被两个对象托管),最后造成2次析构该对象。
|
#include<iostream>
#include<memory>
using namespace std;
class point:public enable_shared_from_this<point>
{
public:
point(int x=0,int y=0):_x(x),_y(y)
{ cout<<"point(int,int)"<<endl; }
~point() { cout<<"~point()"<<endl; }
#if 0
point* add(const point* rhs)
{
_x += rhs->_x;
_y += rhs->_y;
return this;
}
#endif
shared_ptr<point> add(const point* rhs)
{
//在类内部进行托管
_x += rhs->_x;
_y += rhs->_y;
//return shared_ptr<point>(this);
// 和point* add(cosnt point*)一个效果。
return shared_from_this();
//这个方法在enable_shared_from_this<class T>中
}
friend ostream& operator<<(ostream& ,const point&);
private:
int _x;
int _y;
};
ostream& operator<<(ostream& os,const point& rhs)
{
os<<"("<<rhs._x<<","<<rhs._y<<")";
}
|
void test1()//对shared_ptr误用
{ //1、对shared_ptr的误用
point* p1 = new point(1,2);
shared_ptr<point> sp1(p1);
cout<<"sp1 use count="<<sp1.use_count()<<endl;
shared_ptr<point> sp2(p1); //只有sp2 = sp1才会提升引用计数
// shared_ptr<point> sp2(sp1); //这样也会提升引用计数
//重复对一个对象托管,会导致多次调用析构函数
cout<<"sp1 use count="<<sp1.use_count()<<endl;
cout<<"sp2 use count="<<sp2.use_count()<<endl;
}
void test2()
{
shared_ptr<point> p1(new point(1,2));
shared_ptr<point> p2(new point(3,4));
cout<<"p1 use count="<<p1.use_count()<<" p2 use count="<<p2.use_count()<<endl;
p2.reset(p1.get()); // reset先释放p2,在把p1的值赋给p2
cout<<"addr p1="<<p1<<endl;
cout<<"addr p2="<<p2<<endl;
cout<<"p1 use count="<<p1.use_count()<<" p2 use count="<<p2.use_count()<<endl;
}
void test3()
{
shared_ptr<point> p1(new point(1,2));
shared_ptr<point> p2(new point(3,4));
//误用, p1和p3同时托管一个对象
shared_ptr<point> p3(p1->add(p2.get()));
cout<<"p1 use count="<<p1.use_count()<<endl;
cout<<"p2 use count="<<p2.use_count()<<endl;
cout<<"p3 use count="<<p3.use_count()<<endl;
cout<<"p1="<<p1<<" p2="<<p2<<" p3="<<p3<<endl;
}
int main()
{
//test1(); // 这里test1()和test2()同时打开段错误,不知道为啥
cout<<"---------------"<<endl;
//test2(); // 和上面同样的原因,我猜测是前面开辟的堆内存没有释放,后面又要来delete,所以出现错误。
cout<<"---------------"<<endl;
test3();
return 0;
}
|
//point(int,int)
//sp1 use count=1
//sp1 use count=1
//sp2 use count=1
//~point()
//~point()
//---------------
//point(int,int)
//point(int,int)
//p1 use count=1 p2 use count=1
//~point()
//addr p1=0x244e030
//addr p2=0x244e030
//p1 use count=1 p2 use count=1
//~point()
//~point()
//---------------
//point(int,int)
//point(int,int)
//p1 use count=1
//p2 use count=1
//p3 use count=1
//p1=0xbd5030 p2=0xbd5070 p3=0xbd5030
//~point()
//~point()
//~point()
//采用返回shared_from_this()
//---------------
//point(int,int)
//point(int,int)
//p1 use count=2
//p2 use count=1
//p3 use count=2
//p1=0xc2f030 p2=0xc2f070 p3=0xc2f030
//~point()
//~point()
|
#include <iostream>
#include <stdio.h>
#include <memory>
using namespace std;
struct Fpcloser
{
void operator()(FILE * fp)
{
if(fp)
{
cout<<"release file pointer!"<<endl;
fclose(fp);
}
}
};
void test0()
{//自己指定删除器
unique_ptr<FILE,Fpcloser> up( fopen("test.txt","w+"),Fpcloser() );
fputs("hello,world\n",up.get()); //get()返回托管对象的指针
}
void test1()
{//指定删除器的方式
shared_ptr<FILE> sp(fopen("test.txt","r+"),Fpcloser());
char buff[1024];
fgets(buff,sizeof(buff),sp.get());
cout<<buff;
}
|
int main()
{
//test0();
test1();
return 0;
}
|
资源管理
》原理:利用栈对象去管理资源;栈对象的特性是创建对象是自动调用构造函数,当其生命周期结束时,会自动调用析构函数
》RALL
》智能指针
auto_ptr (已被废弃)
unique_ptr
shared_ptr
weak_ptr
最新文章
- Jquery操作select
- 有关DTCoreText无法加载网络图片及应用问题
- float包裹性与破坏性及清除浮动几种方法
- GCC 源码编译 mpc mprf gmp 不用make(否则会有lib/libgmp.so: could not read symbols: File in wrong format等错误)
- 操作系统开发系列—13.a.进程 ●
- BP(back propagation)反向传播
- 发布 PM2.5 数据的城市列表
- Device eth0 does not seem to be present,delaying initialization解决方法
- 创建和使用Windows静态链接库
- php中curl和fsockopen发送远程数据的应用
- CSS 加载新方式
- PaaS平台资源
- 用shell获取文件大小
- android 检测是否插入U盘方法之一
- 从Myeclipse到Intelj Idea
- 纯js无缝滚动
- react-native 插件汇总
- Java获取正在执行的函数名
- WebFlux Spring Security配置
- Network of Schools---poj1236(强连通分量)