• effective c++ 笔记 (13-17)




    //---------------------------15/03/30----------------------------


    //#13   以对象管理资源

    {

       void f()

        {

            Investment *pInv = createInvestment();

            ...

           delete pInv;

        }

    /*  

        1:这里很容易出现内存泄漏:

            1>...中过早地return

            2>...中抛出异常。

        2:为了确保pInv总是被释放,我们需要将资源放进对象内,当控制流离开f时,

        这个对象会自动调用析构函数释放掉资源。

        3:这里有两个常用的智能指针可以实现:

            1>std::auto_ptr。使用方法:std::auto_ptr<Investment> pInv(createInvestment());

            记得不要让多个auto_ptr同时指向一个对象,这样会造成多次delete。为了预防这个问题,auto_ptr

            在被copy给别的指针时,会变成null

            2>tr1::shared_ptr。这个智能指针使用引用计数,在被copy一次后会自动计数加1,调用一次析构函数

            会自动计数减1,当计数为0时销毁对象。

            唯一的问题是循环引用。两个没有被使用的对象相互指着对方,这样看起来好像都在使用中,然而并不是这样。

        4:如果要对申请到的数组资源使用智能指针,可以使用boost::scoped_arrayboost::shared_array

        这两个在析构时调用delete[];

    */

    }


    //#14 在资源管理类中小心coping行为

    {

    //  auto_ptrtr1::shared_ptr不适合掌管非heap-based资源。这时候需要自己建立资源管理类

       class Lock

        {

        public:

           explicit Lock(Mutex* pm): mutexPtr(pm)

            {

                lock(mutexPtr);

            }

            ~Lock()

            {

                unlock(mutexPtr);

            }

        private:

            Mutex *mutexPtr;

        };

        

    /*  这时会出现一个问题:如果对象被复制,这个锁会被再锁上一次。如果是在同一线程中这么做

        程序就死锁了,无法再恢复。所以大多数时候,可以选择以下两种处理。

            1>禁止复制。像Lock这样的class,这样处理是科学的。可以从Uncopyable那用私有继承。

            2>对底层资源使用引用计数法Mutex引用计数版本可以使用tr1::shared_ptr,并

            指定删除器,当引用次数为0时,删除器会被调用。代码看起来像这样:                */

       class Lock

        {

        public:

           explicit Lock(Mutex* pm):mutexPtr(pm, unlock)

            {

                Lock(mutexPtr.get());

            }

        private:

            std::tr1::shared_ptr<Mutex> mutexPtr;

        };

    /*  有时候会有两种特殊的需求:

            1>复制底部资源(深拷贝)。这是为了当你不需要某个复件时确保它被释放,所以拷贝时要连同

            指针指向的资源一起拷贝,不然原始对象删除后就没法用了。

            2>转移底部资源的拥有权。就像auto_ptr一样。

        

    */

    }


    //#15:  在资源管理类中提供对原始资源的访问

    {

    /*  1:在资源管理类中,常常需要提供对原始资源的访问,原因是:一些api需要的就是原始资源

        如果传入一个资源管理类,它们并不起作用。

        2:提供对原始资源访问的方法有二:

            1>提供显示访问:                                                   */

       class Font

        {

        public:

           explicit Font(FontHandle fh): f(fh)

            {}

            ~Font() { releaseFont(f);}

            FontHandle get()const {return f;}

        private:

            FontHandle f;

        };

    /*      这样一来就可以通过调用get()来访问原始资源了,但是每次都要用f.get(),显得十分麻烦。

            2>提供隐式转换函数:

            operator FontHandle() const {return f;}

            但是这么做,存在风险,客户有时候笔误就会造成获得一个错误的类型:                */

        Font f1(getFont());

        ...

        FontHandle f2 = f1;//本来客户是想要 Font f2 = f1;

    /*      但是就算这么调用了,客户也看不出来,因为隐式转换的关系,表达式是可以编译通过的。

            而且使用起来也是一样的。最后,当f1被销毁时,问题来了,f2就成为虚吊的,就是指向

            的资源以及被释放。

        3:两种设计各有优势,为了安全倾向显示转换,为了方便倾向隐式转换。

        4:RAII class返回原始资源与封装发生矛盾,这是真的。但是并没有关系,RAIIclass是为了确保

        资源一定会被释放,而不是为了封装。

    */

    }

     

    //#16  成对使用newdelete时要采用相同形式

    {

    /*  1:当使用new时,有两件事情发生:

            1>内存被分配出来。

            2>针对此内存会调用构造函数(一个或多个)

        2:当使用delete时,也有两件事发生:

            1>先调用析构函数(一个或多个)

            2>释放内存。

        3:数组对象的内存中一般还包含一个数组大小的记录,以便delete知道需要调用多少次析构函数

        所以在调用时必须成对出现,不然会发生不可预测的情况。

        4:使用typedef时,最好不要对数组进行typedef

        比如: typedef std::string AddressLines[4];

        这样很容易错误地使用delete,所以应该避免这种定义。                            */

    }

     

    //#17   以独立的语句将newed对象置入智能指针

    {

    //  看一个函数调用:

        processWidget(std::tr1::shared_ptr<Widget>(new Widget), priority());


    /*  这样调用有可能造成内存泄漏,首先,编译器为了效率问题执行以下顺序:

            1>new widget;

            2>priority();

            3>tr1::shared_ptr的构造函数

        如果priority()的调用导致异常,那么new Widget返回的指针就遗失了,内存就泄漏了。

        所以为了避免着问题很简单:                                               */

        std::tr1::shared_ptr<Widget> pw(new Widget);

        

        processWidget(pw, priority());

    //  newed对象置入智能指针的操作用独立语句来实现,这样就不会产生内存泄漏了。

    }








  • 相关阅读:
    vue+element-ui实现前端分页
    element-UI中table表格的row-click事件怎么获取一行数据的id
    使用一个for循环将N*N的二维数组的所有值置1
    http常见的状态码
    反转一个英文句子中的单词,并且对应位置大小写不改变
    用一条SQL语句查出每门课都大于80分的学生的姓名
    平滑重启原理及平滑更新
    php之命名空间
    php之trait-实现多继承
    C入门之一
  • 原文地址:https://www.cnblogs.com/boydfd/p/4983152.html
Copyright © 2020-2023  润新知