Effective C++ 5.实现

2024-06-08 13:18
文章标签 c++ 实现 effective

本文主要是介绍Effective C++ 5.实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

条款26:尽可能延后变量定义式的出现时间

    只要你定义了一个变量而其类型带有一个构造函数或析构函数,那么当程序的控制流到达这个变量定义式时,你便得承受构造成本;当这个变量离开其作用域时,你便得承受析构成本。即使这个变量最终并为被使用,仍需耗费这些成本,所以应该尽量避免这种情形。
    std::string encryptPassword(const std::string& password)
    {
        using namespace std; 
        string encrypted1;
        if (password.length() < MinimumPasswordLength)
        {
            throw logic_error("Password is too short");     //注意:可能抛出异常
        }
        string encrypted2;
         ...
        return encrypted;
     }
    如上代码,encrypted在2处定义是个不错的选择,因为如果抛出异常,那么encrypted的构造和析构可是做了无用功啊!
     还有一点要注意:“通过默认构造函数构造出一个对象然后对它赋值”比“直接在构造函数时制定初值”效率差。
     “尽可能延后”的真正意义应该是:你不只应该延后变量的定义,直到非得使用该变量的前一刻为止,甚至应该尝试延后这份定义直到能够给它初值实参为止。
     //方法A:定义循环外
    Widget w;
     for (int i = 0; i < n; ++i)
    { 
        w = some value dependent on i;
        ...
    }     //1个构造函数+1个析构函数+n个赋值操作;
    //方法B:定义循环外
     for (int i = 0; i < n; ++i)
    {
        Widget w(some value dependent on i); 
        ...
    }     //n个构造函数+n个析构函数
    除非:1.你知道赋值成本比“构造+析构”成本低;2.你正在处理代码中效率高度敏感的部分,否则应该使用方法B
    请记住:

  • 尽可能延后变量定义式的出现。这样做可增加程序的清晰度并改善程序效率。   

    条款27:尽量少做转型动作
    C++规则的设计目标之一是,保证“类型错误”绝不可能发生。不幸的是,转型(casts)破坏了类型系统。那可能导致任何种类的麻烦,有些容易辨识,有些非常隐晦。
    C风格的转型动作看起来像这样:
     (T)expression    //将expression转型为T
     函数风格的转型动作看起来像这样:
     T(expression)    //将expression转型为T
    C++还提供四种新式转型:
     const_cast:通常被用来将对象的常量性转除;即去掉const。
     dynamic_cast:主要用来执行“安全向下转型”,也就是用来决定某对象是否归属继承体系中的某个类型。
     reinterpret_cast:意图执行低级转型,实际动作可能取决于编译器,这也就表示它不可移植。
     static_cast:用来强迫隐式转换,例如将non-const转型为const,int转型为double等等。
    尽量使用新式转型:

  • 它们很容易在代码中被辨识出来,因而得以简化“找出类型系统在哪个地点被破坏”的过程。
  • 各转型动作的目标愈窄化,编译器愈可能诊断出错误的运用。   

    请记住:

  • 如果可以,尽量避免转型,特别是在注重效率的代码中避免dynamic_casts。如果有个设计需要转型动作,试着发展无需转型的替代设计。
  • 如果转型是必要的,试着将它隐藏于某个函数背后。客户随后可以调用该函数,而不需将转型放进他们自己的代码内。
  • 宁可使用C++-style(新式)转型,不要使用旧式转型。前者很容易辨识出来,而且也比较有着分门别类的执掌。   

    条款28:避免返回handls指向对象内部成分
    struct RectData
     {
        Point ulhc;
        Point lrhc;
     };
    class Rectangle
    {
        public:
            ... 
            Point& upperLeft() const { return pData->ulhc; }1//const只对函数内进行保护,函数返回后呢??
            Point& lowerRight() const { return pData->lrhc; }2 
//const只对函数内进行保护,函数返回后呢??
        private:
            std::tr1::shared_ptr<RectData> pData;
            ...
    };
    1,2两函数都返回引用,指向private内部数据,调用者于是可通过这些引用更改内部数据!这严重破坏了数据的封装性,对私有成员进行直接操作?太不可思意了!
     const Point& upperLeft() const { return pData->ulhc; }3
       const Point& lowerRight() const { return pData->lrhc; }
4     
      或者将1,2改为3,4,这就限制了客户的“涂改权”,只有“读取权”。
    但终究“返回一个handle代表对象内部成分”总是危险的。特别是将返回的指针或引用赋值给其它指针或引用,那么久造成了“悬空”。
    请记住:

  • 避免返回handles(包括reference、指针、迭代器)指向对象内部。遵守这个条款可增加封装性,帮助const成员函数的行为像个const,并将发生“虚吊号码牌”(dangling handles)的可能性降至最低。   

    条款29:为“异常安全”而努力是值得的
    请记住:

         1. 以对象管理资源,可以有效的避免异常的抛出,比如可以用智能指针来管理对象。

        

      2.  copy and swap   : 为你打算修改的对象(原件)做出一份副本,然后再那个副本上做一切必要的修改,
若修改的对象抛出异常,原对象仍保持未改变状态。
待所有改变都成功后,再将修改过的那个副本和原对象
在一个不抛出异常的操作中置换。
       
       struct PMImp1{
       std::tr1::shared_ptr<Image>  bgImage;
       int imageChanges;
       };

       class PrettyMenu
       {
           ...
          private:
          Mutex   mutex;
           std::tr1::shared_ptr<PMImp1>  pImp1;
      };

      void PrettyMenu::changeBackgroud(std::istream& imgSrc)
       {
              using std::swap;
               Lock  m1(&mutex);
                     std::tr1::shared_ptr<PMImp1>  pNew(new PMImp1(*pImp1));
                    
                      pNew->bgImage.reset(new Image(imgSrc));
                      ++pNew->imageChanges;
                    
                    swap(pImp1, pNew);
       }

  • 异常安全函数(Exception-safe functions)即使发生异常也不会泄漏资源或允许任何数据结构败坏。这样的函数区分为三种可能的保证:基本型、强烈型、不抛异常型。
  • “强烈保证”往往能够以copy-and-swap实现出来,但“强烈保证”并非对所有函数都可实现或具备现实意义。
  • 函数提供的“异常安全保证”通常最高只等于其所调用之各个函数的“异常安全保证”中的最弱者。   

    条款30:透彻了解inlining的里里外外
    Inline 函数,多棒的点子!它们看起来像函数,动作像函数,比宏好得多,可以调用它们又不需蒙受函数调用所招致的额外开销。你实际获得的比想象的还多,编译器有能 力对执行语境相关最优化。然而编写程序就像现实生活一样,没有白吃的午餐。inline函数也不例外,这样做可能增加你的目标码。
     如果inline函数的本体很小,编译器针对“函数本体”所产生的码可能比针对“函数调用”所产出的码更小。果真如此,将函数inlining确实可能导致较小的目标码和较高的指令高速缓存装置击中率。
     记住,inline只是对编译器的一个申请,不是强制命令。这项申请可以隐喻提出,也可以明确提出。隐喻方式是将函数定义于class定义式内,这样的函数通常是成员函数,friend函数也可被定义于class内,如果真是那样,它们也是被隐喻声明为inline。明确声明inline函数的做法则是在其定义式钱加上关键字inline
     Inline函数通常一定被置于头文件内,因为大多数建置环境在编译过程中进行inlining,而为了将一个“函数调用”替换为“被调用函数的本体”,编译器必须知道那个函数长什么样子。
     Template通常也被置于头文件内,因为它一旦被使用,编译器为了将它具现化,需要知道哦啊它长什么样子。
     Template的具现化与inlining无关。如果你正在写一个template而你认为所有根据此template具现出来的函数都应该 inlined,请将此template声明为inline;但如果你写的template煤油理由要求它所具现的每一个函数都是inlined,就应该 避免将这个template声明为inline。
     一个表面上看似inline的函数是否真实inline,取决于你的建置环境,主要取决于编译器。
     有的时候虽然编译器有意愿inlining某个函数,还是可能为该函数生成一个函数本体(函数指针,构造函数,析构函数)。
     对程序开发而言,将上述所有考虑牢记在新很是重要,但若从纯粹实用观点出发,有一个事实比其它因素更重要:大部分调试器面对inline函数都束手无策。
     这使我们在决定哪些函数该被声明为inline而哪些函数不该时,掌握一个合乎逻辑的策略。一开始先不要将任何函数声明为inline,或至少将inlining施行范围局限在那些“一定成为inline”或“十分平淡无奇”的函数身上。
     请记住:

  • 将大多数inlining限制在小型、被频繁调用的函数身上。这可使日后的调试过程和二进制升级更容易,也可使潜在的代码膨胀问题最小化,是程序的速度提升机会最大化。
  • 不要只因为function templates出现在头文件,就将它们声明为inline。   

     条款31:将文件间的编译依存关系降至最低
     请记住:

  • 支持“编译依存性最小化”的一般构想是:相依于声明式,不要相依于定义式。基于此构想的两个手段是Handle classed和Interface classes。
  • 程序库头文件应该以“完全且仅有声明式”(full and declaration-only forms)的形式存在。这种做法不论是否涉及templates都适用。

这篇关于Effective C++ 5.实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1042286

相关文章

windos server2022里的DFS配置的实现

《windosserver2022里的DFS配置的实现》DFS是WindowsServer操作系统提供的一种功能,用于在多台服务器上集中管理共享文件夹和文件的分布式存储解决方案,本文就来介绍一下wi... 目录什么是DFS?优势:应用场景:DFS配置步骤什么是DFS?DFS指的是分布式文件系统(Distr

NFS实现多服务器文件的共享的方法步骤

《NFS实现多服务器文件的共享的方法步骤》NFS允许网络中的计算机之间共享资源,客户端可以透明地读写远端NFS服务器上的文件,本文就来介绍一下NFS实现多服务器文件的共享的方法步骤,感兴趣的可以了解一... 目录一、简介二、部署1、准备1、服务端和客户端:安装nfs-utils2、服务端:创建共享目录3、服

C#使用yield关键字实现提升迭代性能与效率

《C#使用yield关键字实现提升迭代性能与效率》yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,本文主要来聊聊如何使用yield关键字实现提升迭代性能与效率,感兴... 目录前言传统迭代和yield迭代方式对比yield延迟加载按需获取数据yield break显式示迭

Python实现高效地读写大型文件

《Python实现高效地读写大型文件》Python如何读写的是大型文件,有没有什么方法来提高效率呢,这篇文章就来和大家聊聊如何在Python中高效地读写大型文件,需要的可以了解下... 目录一、逐行读取大型文件二、分块读取大型文件三、使用 mmap 模块进行内存映射文件操作(适用于大文件)四、使用 pand

python实现pdf转word和excel的示例代码

《python实现pdf转word和excel的示例代码》本文主要介绍了python实现pdf转word和excel的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一、引言二、python编程1,PDF转Word2,PDF转Excel三、前端页面效果展示总结一

Python xmltodict实现简化XML数据处理

《Pythonxmltodict实现简化XML数据处理》Python社区为提供了xmltodict库,它专为简化XML与Python数据结构的转换而设计,本文主要来为大家介绍一下如何使用xmltod... 目录一、引言二、XMLtodict介绍设计理念适用场景三、功能参数与属性1、parse函数2、unpa

C#实现获得某个枚举的所有名称

《C#实现获得某个枚举的所有名称》这篇文章主要为大家详细介绍了C#如何实现获得某个枚举的所有名称,文中的示例代码讲解详细,具有一定的借鉴价值,有需要的小伙伴可以参考一下... C#中获得某个枚举的所有名称using System;using System.Collections.Generic;usi

Go语言实现将中文转化为拼音功能

《Go语言实现将中文转化为拼音功能》这篇文章主要为大家详细介绍了Go语言中如何实现将中文转化为拼音功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 有这么一个需求:新用户入职 创建一系列账号比较麻烦,打算通过接口传入姓名进行初始化。想把姓名转化成拼音。因为有些账号即需要中文也需要英

C# 读写ini文件操作实现

《C#读写ini文件操作实现》本文主要介绍了C#读写ini文件操作实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录一、INI文件结构二、读取INI文件中的数据在C#应用程序中,常将INI文件作为配置文件,用于存储应用程序的

C#实现获取电脑中的端口号和硬件信息

《C#实现获取电脑中的端口号和硬件信息》这篇文章主要为大家详细介绍了C#实现获取电脑中的端口号和硬件信息的相关方法,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 我们经常在使用一个串口软件的时候,发现软件中的端口号并不是普通的COM1,而是带有硬件信息的。那么如果我们使用C#编写软件时候,如