正在加载……
专注、离线、切勿分心
资源管理--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






posted on 2018-05-29 10:40  正在加载……  阅读(920)  评论(0编辑  收藏  举报