RALL资源获取初始化,删除器
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;}
1、在构造时初始化资源, 或托管已构造的资源
2、析构时释放资源
3、一般不允许复制或赋值(对象语义)
4、提供若干访问资源的方法
|
#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;
}
|
std::auto_ptr(复制/赋值) 现在已淘汰
std::unique_ptr c++11
std::shared_ptr c++11
std::weak_ptr c++11
g++ -std=c++11 xx.cc
|
#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;
}
|
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;
}
|
#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;
}
|
#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;
}
|
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;
}
|
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资源获取初始化,删除器的更多相关文章
- C++进阶--RAII 资源获取即初始化
//############################################################################ /* 资源获取即是初始化 (RAII) * ...
- ServletContext类 (共享数据+获取初始化的参数+请求转发+读取资源文件)
ServletContext对象 web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的 web应用: 作用 1.共享数据 (一般用sessio ...
- WebGL 创建和初始化着色器过程
1.编译GLSL ES代码,创建和初始化着色器供WebGL使用.这些过程一般分为7个步骤: 创建着色器对象(gl.createBuffer()); 向着色器对象中填充着色器程序的源代码(gl.shad ...
- Spark源码剖析 - SparkContext的初始化(八)_初始化管理器BlockManager
8.初始化管理器BlockManager 无论是Spark的初始化阶段还是任务提交.执行阶段,始终离不开存储体系.Spark为了避免Hadoop读写磁盘的I/O操作成为性能瓶颈,优先将配置信息.计算结 ...
- C++几个技巧:智能指针在消息传递中的使用,元组,及lambda删除器
1.SendMessage/PostMessage中传递对象参数 (1)方法1:使用shared_ptr 发送端: PostMessage(MyhWnd, CWM_SOME_ERROR, 0, rei ...
- iOS UIWebView 和 WKWebView 的 cookie 获取,设置,删除
Cookie简介说到Cookie,或许有些小伙伴会比较陌生,有些小伙伴会比较熟悉.如果项目中,所有页面都是纯原生来实现的话,一般Cookie这个东西或许我们永远也不会接触到.但是,这里还是要说一下Co ...
- Java并发包源码学习系列:CLH同步队列及同步资源获取与释放
目录 本篇学习目标 CLH队列的结构 资源获取 入队Node addWaiter(Node mode) 不断尝试Node enq(final Node node) boolean acquireQue ...
- cookies的获取,删除,设置
cookies,sessionStorage 和 localStorage 的区别? 1.cookie在浏览器和服务器间来回传递. sessionStorage和localStorage不会: 2.s ...
- SpringMVC核心——参数获取与Servlet资源获取问题
一.SpringMVC 使用 @PathVariable.@RequestParam.@RequestHeader.@CookieValue 等来解决参数获取问题. 1. @PathVariable: ...
随机推荐
- sql server中带有output的DML
OUTPUT是SQL SERVER2005的新特性.可以从数据修改语句中返回输出.可以看作是"返回结果的DML".INSERT,DELETE,UPDATE均支持OUTPUT子句.在 ...
- Docker Compose 入门使用指南
Compose is a tool for defining and running multi-container Docker applications. With Compose, you us ...
- PAT 1057 Stack [难][树状数组]
1057 Stack (30)(30 分) Stack is one of the most fundamental data structures, which is based on the pr ...
- Ubuntu 16.04安装Eclipse并创建桌面快捷方式
系统:Ubuntu 16.04 JDK版本:1.8.0_121 1.官网下载eclipse,我的版本是eclipse-jee-neon-2-linux-gtk-x86_64.tar.gz,只要JDK版 ...
- IBM WebSphere MQ for net 报错 MQRC_NOT_AUTHORIZED
最近进入新公司要维护以前的90年代的老系统 用NET对IBMMQ做测试 NET 4.0 +7.5 MQ 版本 待我写好NET调用的代码后出现错误MQRC_NOT_AUTHORIZED 折腾大半天往上找 ...
- zw版【转发·台湾nvp系列Delphi例程】HALCON HSerializedItem
zw版[转发·台湾nvp系列Delphi例程]HALCON HSerializedItem procedure TForm1.FormShow(Sender: TObject);var img, im ...
- ng-深度学习-课程笔记-16: 自然语言处理与词嵌入(Week2)
1 词汇表征(Word representation) 用one-hot表示单词的一个缺点就是它把每个词孤立起来,这使得算法对词语的相关性泛化不强. 可以使用词嵌入(word embedding)来解 ...
- word 杂记
45.error和exception有什么区别? 答案:Error表示系统级的错误和程序不必处理的异常,我们无法处理它. Exception表示是可以捕捉或者需要程序进行处理的异常. 47.abstr ...
- Linux学习笔记之Linux最小化安装启动后如何配置
在VM虚拟机中安装CentOS 7 时 有时候顾虑到电脑硬件性能,我们需要最小化安装,而最小化安装后与centos6的版本是有一些差异的,接下来我们就对刚安装好的最小化centos7做一些操作,来世我 ...
- windows10添加开机自启动程序
1. 将要启动的程序创建快捷方式 2. 将创建的快捷方式放到以下文件夹中 C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup