美文网首页
资源管理、设计与声明

资源管理、设计与声明

作者: szn好色仙人 | 来源:发表于2018-03-22 20:15 被阅读0次
    条款13 以对象管理资源
    • C++程序中最常使用的资源就是动态分配内存,并且还包括文件描述器,互斥锁,GDI对象、数据库连接、网络socket等。不管哪一种资源,当不再使用的时候必须将其归还给系统。
    • 考虑以下Fun函数,若动态分配内存后,不小心使用了return、goto、或者发生了异常,就有可能导致分配的内存不会被释放。当然,谨慎的编写程序可以避免上述错误,但是必须考虑到,代码可能在时间渐渐过去后被修改,一旦软件开始接受维护,可能会被添加欠缺考虑的return等语句而导致delete不能被正常执行。为了确保资源被正确释放,正确的做法可以是将资源放入一个对象内,并让该对象的析构函数会自动释放那些资源(注意条款08 别让异常逃出析构函数)。
    void Fun()
    {
        int* pBuff = new int(10);
        ... //干点别的事情
        delete pBuff;
    }
    
    • 利用智能指针可以方便的实现利用对象来管理动态内存资源。经过测试,当需要考虑效率的时候,不如将资源放入一个简单的自定义类中进行管理(构造函数中获取资源,析构函数中释放资源)。

    条款14 在资源类中小心coping行为
    • 禁止复制。如同类unique_ptr那样,比如一个资源类包含了互斥锁,那么这个类就应该禁止复制。通过将一系列的拷贝操作声明为private并且不进行定义来实现禁止复制。
    • 引用计数法。如同类shared_ptr那样,将一直有资源,直到它的最后一个使用者被销毁。实际上使用shared_ptr类并传入自己的删除器可以很方便的实现对资源进行引用计数法管理。
    • 复制底部资源。有时候需要针对一份资源,需要拥有其任意数量的副本,此时进行对资源的深拷贝。
    • 转移底部资源。将资源从一个拥有者转移给另一个拥有者。即移动的概念。当对unique_ptr进行std::move的时候就是这种情况。

    条款15 在资源类中提供对原始资源的直接访问
    • 存在很多函数需要访问原始资源,所以每个资源类都应该提供对原始资源直接访问的方法,可以是显示转换(比如C++智能指针的get()),也可以使用隐式转换
    • C++智能指针对->和*进行了重载,它们允许隐式转换至底部原始指针。
    unique_ptr<string> pStr(new string("szn"));
    const auto nSizeC = pStr->size();    //nSizeC = 3
    
    条款16:成对使用new和delete时要采取相同的形式。
    • 使用new[]分配内存的时候,会有多个构造函数被调用,当不慎使用delete删除new[]分配的内存时,对应的析构函数的调用次数会出错。
    • 当使用typedef的时候特别要小心:
    typedef int Int10[10];
    int* pTem = new Int10;
    delete[]  pTem;  
    
    条款17 以独立的语句将new出来的对象放入智能指针中
    • 考虑如下语句:
      Fun0(shared_ptr<int>(new int), Fun1());
      由于函数形参的调用时机不同,上述函数的调用方式可能是这样的:首先执行new int,然后执行Fun1(),最后执行shared_ptr的构造函数,当在Fun1()中产生异常,则将导致内存泄露。
      避免上述问题的方法很简单:
    shared_ptr<int> pInt(new int);
    Fun0(pInt, Fun1());
    
    条款18 让接口容易被正确使用,不易被误用
    • 如果客户企图使用某个接口而却没有获得他所预期的行为,那么这个代码就不该通过编译。
    • 促进正确使用的方法包括接口的一致性,以及与内置类型的行为兼容。
    • 阻止误用的方法包括建立新类型、限制类型上的操作,束缚对象值,以及消除客户的资源管理责任。

    条款20 pass by reference to const 替换 pass by value
    • C++的底层操作会将传引用操作以指针的形式来实现。
    • 传递const的引用可以避免父对象接收子对象时候面临的子对象被切割的问题。
    • 传递const的引用可以有效避免无谓的拷贝和销毁操作,在性能上产生优势
    • 对于内置类型以及STL的迭代器以及函数对象,对它们而言传递值比传递引用往往更加适当。

    条款21 必须返回对象时,别妄想返回引用
    • 绝不要返回指向一个存在于栈中的对象的指针或引用,或返回一个存在于堆的对象的引用(需自己管理资源),亦或是返回一个指向在栈中声明并定义的static对象的引用。当必须返回对象的时候,老实的创建一个对象返回就好了。至于是不是必须要返回对象,要想想是否有其他可能来避免返回一个对象(传参进去喽)

    条款22 将成员变量声明为private
    • 将成员变量声明为private的,这可以赋予客户访问数据的一致性、可细微划分访问控制、允诺约束条件获得保证,并让类的设计者以充分的实现弹性。
    • 切记protected并不比public更具封装性。若子类以public方式继承,则可以通过using轻松改变父类中protected成员的访问权限。而且在派生类中,派生类也可以直接使用protected变量,这也给基类的封装性带来巨大的打击
    • 即使拥有类的源码,改变任何public事物的能力仍极端受束缚,因为会破坏太多客户代码。public意味着不封装,即意味着不可改变

    条款23 以非成员函数、非友元函数替换成员函数
    • 越少的代码可以访问到类的私有成员数据,那么类的封装性就越好
    • 如果一个类包含很多可复用的工具函数,考虑下这些函数是不是非要定义为类的成员函数。一个将数据以及操作数据的函数捆绑在一起组成一个classEverything的类,其实不是一个很好的习惯。在C++中较自然的做法是将一个类以及此类的辅助函数放在一个命名空间中,而不是让这些辅助函数定义为类的成员函数

    条款24 若所有参数都可能需要类型转换,那么最好将其声明为类的非成员函数
    • 经典的例子是类的operator *(...)等算术操作

    条款25 考虑为类写出一个不抛出异常的swap函数
    • swap是一个有趣的函数,原本是STL的一部分,而后来成为异常安全性变成的脊柱,以及用来处理自我赋值的可能性。
    • 当std::swap对自定义的类型效率不高的时候(比如在这个类型中使用了指针指向了一个内存,如此swap应该交换指针而非交换指针所指对象),提供一个swap成员函数,并确定这个函数不抛出异常
    • 如果提供一个成员函数swap,那么也该提供一个非成员函数版本的swap来调用成员函数版本的swap。
    • 不要试图往std命名空间中添加新的成员,这种行为是未定义的。但是对于std命名空间中的模板进行特例化也是挺好的
    • 本章建议再看原书,这里只是简单的归纳了下

    相关文章

      网友评论

          本文标题:资源管理、设计与声明

          本文链接:https://www.haomeiwen.com/subject/owlzqftx.html