【读书笔记】Effective C++(04)设计与声明

做者:LogMsegmentfault

本文原载于 https://segmentfault.com/u/logm/articles,不容许转载~安全

4. 设计与声明

  • 4.1 条款18:让接口不易被误用

    • 简单来讲,就是考虑用户可能的误用行为,在代码中规避。好比工厂函数返回值为"智能指针"类型,避免用户使用通常指针管理资源带来的资源泄漏风险;好比将 operator* 的返回类型设为const,避免用户写出"a*b=c"这样的代码。
  • 4.2 条款19:像设计type同样设计class

    • 做者的意思是,设计class要考虑不少细节,尽可能使设计出来的class像C++内置类型同样有极大的可用性和鲁棒性。
  • 4.3 条款20:宁以 pass-by-reference-to-const 替换 pass-by-value

    • 缘由:a. 参数使用引用传递,不须要构造新对象,比较快;b. 避免对象切割问题。
    • 对象切割(slicing):当派生类对象以 by-value 方式传递参数并被视为基类对象,基类的拷贝构造函数在构造时会把派生类特有的性质抹除。
    • "引用"在编译器底层的实现就是指针(指针的本质是int类型的变量),因此在如下场景,"引用"并不必定比pass-by-value快:a. C++内置类型;b. STL的迭代器(底层实现是指针);c. 函数对象(底层实现是指针)。
  • 4.4 条款21:不要让函数返回值是指向局部变量的引用或指针

    • 缘由应该很容易理解:局部变量在函数调用结束后就销毁了,那么这个函数返回的引用和指针指向的内存已经无效了。
  • 4.5 条款22:将成员变量声明为 private

    • 一致性:成员变量为 private,则用户想访问成员变量必须经过成员函数,因此用户就不用试着记住是否是要加括号,由于成员函数都要加括号。
    • 安全性:经过成员函数控制用户对成员变量的读写权限。
    • 封装性:class的版本发生变化,但提供给用户的API仍是能够保持不变。
  • 4.6 条款23:宁以 non-menber、non-friend 替换 member 函数

    • 使用场景以下代码所示。做者倾向于non-member、non-friend的理由是:它们没法访问private的成员变量,在封装性上更好,编译时候的依赖程度也低。
    • 做者的说法有必定道理,但我不彻底赞成做者的观点:cookie

      • a. member函数能够访问private的成员变量,并不意味着用户就能够接触到private的成员变量,你在写代码的时候不让这个member函数访问private的成员变量不就能够了?(此时问题变成了:如何确保写代码的人不在这个函数中滥用private的成员变量)
      • b. 有些状况,使用non-member、non-friend函数会下降代码接口的一致性。做者的解决思路是把non-member、non-friend函数放在和类同一个namespace下,我想了想,这么作一致性仍是不如直接写member函数。
    • class WebBrowser {
      public:
          ...
          void clearCache();
          void clearHistory();
          void clearCookies();
          ...
      }
      
      //假如如今要写一个函数clearEverything(),做用是同时清理cache、history、cookies。
      
      //使用member函数的状况
      class WebBrowser {
      public:
          ...
          void clearEverything();
          ...
      }
      void WebBrowser::clearEverything() {
          clearCache();
          clearHistory();
          clearCookies();
      }
      
      //使用non-member函数的状况
      void clearBrower(WebBrowser& wb) {
          wb.clearCache();
          wb.clearHistory();
          wb.clearCookies();
      }
  • 4.7 条款24:若全部参数都须要类型转换,请把这个函数写成 non-member 函数

    • //第一种状况:乘法函数为member函数
      class Rational {
      public:
          ...
          Rational(int numerator, int denominator);
          Rational(int num);  //这个构造函数使得该类支持从int到Rational的类型转换。若是前面加explict则说明不支持隐式类型转换仅支持显式转换,如今没加,支持隐式转换
          const Rational operator* (const Rational& rhs) const;
      }
      
      //使用
      Rational lhs(1, 9);
      Rational result;
      result = lhs * 2;   //ok,2不是Rational类型,但能够发生隐式类型转换
      result = 2 * lhs;   //bad,2不是Rational类型
    • //第二种状况:乘法函数为non-member函数
      class Rational {
      public:
          ...
          Rational(int numerator, int denominator);
          Rational(int num);  //这个构造函数使得该类支持从int到Rational的类型转换
      }
      
      const Rational operator* (const Rational& lhs, const Rational& rhs) {
        ...
      }
      
      //使用
      Rational lhs(1, 9);
      Rational result;
      result = lhs * 2;   //ok,2不是Rational类型,但能够发生隐式类型转换
      result = 2 * lhs;   //ok,2不是Rational类型,但能够发生隐式类型转换
  • 4.8 条款25:考虑写出一个不抛异常的swap函数

    • STL库中std::swap的典型实现以下代码,这个实现比较平淡。对于某些类,写一个模板特化的swap执行效率会更高。做者介绍了怎么本身写std::swap的特化版本,这边就不展开了。
    • namespace std {   //平淡的std::swap实现
          template<typename T>
          void swap(T& a, T& b) {
              T temp(a);
              a = b;
              b = temp;
          }
      }
相关文章
相关标签/搜索