EffectiveC++入门:四大习惯提升代码质量

好的,这是一份关于《Effective C++》第一部分"让自己习惯C++"的读书总结要点:

第一部分:让自己习惯C++

条款01:视C++为一个语言联邦

  • C++ 是多范式编程语言,可视为由多个"子语言"组成的联邦:
    • C:C++ 的基础,包含基础语法、数据类型、预处理等。
    • 面向对象 C++:类、封装、继承、多态、虚函数等。
    • 模板 C++:泛型编程,模板元编程。
    • STL:标准模板库,包含容器、迭代器、算法和函数对象。
  • 要点:在不同"子语言"下切换时,可能需要遵循不同的编程规则。理解这些子语言及其交互是高效使用 C++ 的关键。

条款02:尽量以 const, enum, inline 替换 #define

  • 问题 :宏 (#define) 在预处理阶段进行简单文本替换,缺乏类型检查和作用域概念,可能导致难以理解的错误。

  • 替代方案

    • 常量定义 :用 const 定义常量(全局或类内)。

      cpp 复制代码
      const double Pi = 3.14159; // 优于 #define Pi 3.14159
    • 类内常量 :对于类专属常量,可声明为 static const

      cpp 复制代码
      class Widget {
      private:
          static const int MinSize = 5; // 常量声明
      };
    • 枚举值 :当需要在类内定义一组相关整数常量时,可用 enum("the enum hack")。

      cpp 复制代码
      class Widget {
      private:
          enum { MinSize = 5 }; // 编译器通常不分配内存
      };
    • 函数宏 :用 inline 函数替换函数宏,以获得类型安全、参数计算、作用域和访问控制。

      cpp 复制代码
      template<typename T>
      inline T max(const T& a, const T& b) { // 优于 #define max(a, b) ((a) > (b) ? (a) : (b))
          return a > b ? a : b;
      }
  • 要点 :优先使用语言层面的 const, enum, inline 而非预处理器宏,以获得更好的安全性、可维护性和调试便利性。

条款03:尽可能使用 const

  • const 的作用:表达语义约束("不该被改动"),编译器强制执行。
  • 应用场景
    • 修饰变量/对象 :声明常量,防止修改。

      cpp 复制代码
      const int DaysInWeek = 7;
      const std::string authorName("Scott Meyers");
    • 修饰指针

      • const char* p:指向常量的指针(指针可变,指向的内容不可变)。
      • char* const p:常量指针(指针不可变,指向的内容可变)。
      • const char* const p:指向常量的常量指针(指针和内容都不可变)。
    • 修饰迭代器

      • std::vector<int>::const_iterator cit:指向常量的迭代器(类似 const T*)。
      • const std::vector<int>::iterator it:常量迭代器(类似 T* const)。
    • 修饰函数参数和返回值:防止传入的参数在函数内被修改,或防止函数返回值被意外修改(较少用)。

    • 修饰成员函数

      • 目的 :表明该成员函数不会修改对象的非静态成员变量(mutable 成员除外)。
      • 语法 :在成员函数声明的参数列表后加上 const
      cpp 复制代码
      class TextBlock {
      public:
          const char& operator[](std::size_t position) const { // 用于 const 对象
              return text[position];
          }
          char& operator[](std::size_t position) { // 用于 non-const 对象
              return text[position];
          }
      private:
          std::string text;
      };
      • 意义const 对象只能调用 const 成员函数。通过提供 const 和 non-const 版本的重载成员函数,可以在 const 正确性(避免错误修改)和非 const 对象的使用便利性(允许修改)之间取得平衡。
    • mutable :用于修饰成员变量,即使在 const 成员函数中,mutable 成员也可以被修改。

  • 要点 :大胆使用 const,它有助于编译器检测错误,增强代码可读性(表达设计意图),并支持 const 对象的使用。理解 const 在指针、迭代器和成员函数中的应用至关重要。

条款04:确定对象被使用前已先被初始化

  • 问题:未初始化的对象(特别是内置类型)的值是未定义的,使用它们会导致未定义行为(UB)。

  • 初始化方法

    • 手动初始化内置类型 :在定义时赋值。

      cpp 复制代码
      int x = 0; // 手动初始化
    • 构造函数初始化列表 :类成员变量的初始化应通过构造函数初始化列表进行,而不是在构造函数体内赋值。

      cpp 复制代码
      class PhoneNumber { /* ... */ };
      class ABEntry {
      public:
          ABEntry(const std::string& name, const std::string& address, const std::list<PhoneNumber>& phones);
      private:
          std::string theName;
          std::string theAddress;
          std::list<PhoneNumber> thePhones;
          int numTimesConsulted;
      };
      ABEntry::ABEntry(const std::string& name, const std::string& address, const std::list<PhoneNumber>& phones)
          : theName(name), // 初始化列表
            theAddress(address),
            thePhones(phones),
            numTimesConsulted(0) // 内置类型也要初始化!
      {
          // 构造函数体(此时成员已初始化完毕,这里是赋值)
      }
      • 优点 :效率更高(避免先调用默认构造函数再赋值的开销),尤其对于 const 和引用成员(必须在初始化列表中初始化)。
    • 类内成员初始化 (C++11) :在类定义中直接为成员变量指定初始值。

      cpp 复制代码
      class ABEntry {
      private:
          std::string theName = "";
          std::string theAddress = "";
          std::list<PhoneNumber> thePhones{};
          int numTimesConsulted = 0;
      };
      • 优点:代码更清晰,减少构造函数初始化列表的负担,避免遗漏初始化。
    • static 对象初始化

      • 函数内的 static 对象 (local static):在第一次调用该函数时初始化(线程安全需考虑)。
      • 非函数内的 static 对象 (non-local static) :在 main 开始前初始化,顺序不确定(不同编译单元间)。
      • 问题:不同编译单元中 non-local static 对象的初始化顺序不确定。如果某个 non-local static 对象依赖另一个 non-local static 对象,而后者尚未初始化,会导致问题。
      • 解决方案:将 non-local static 对象替换为函数内的 local static 对象(单例模式常用)。
      cpp 复制代码
      class FileSystem { /* ... */ };
      FileSystem& tfs() { // 用函数代替直接访问
          static FileSystem fs; // local static 对象
          return fs;
      }
  • 要点 :永远确保所有对象在使用前已被初始化。对于类成员,优先使用构造函数初始化列表或类内成员初始化。对于 static 对象,注意初始化顺序问题,用 local static 对象替代 non-local static 对象可以解决跨编译单元的初始化依赖问题。

总结第一部分: 第一部分奠定了编写良好 C++ 代码的基础习惯:理解 C++ 的多范式特性、避免宏、善用 const 保证正确性、以及始终确保对象的正确初始化。这些习惯能有效避免许多常见错误,提高代码的健壮性和可维护性。

相关推荐
秋邱12 小时前
用 Python 写出 C++ 的性能?用CANN中PyPTO 算子开发硬核上手指南
开发语言·c++·python
我在人间贩卖青春13 小时前
C++之析构函数
c++·析构函数
我在人间贩卖青春13 小时前
C++之数据类型的扩展
c++·字符串·数据类型
wangjialelele13 小时前
平衡二叉搜索树:AVL树和红黑树
java·c语言·开发语言·数据结构·c++·算法·深度优先
苏宸啊14 小时前
C++栈和队列
c++
森G14 小时前
七、04ledc-sdk--------makefile有变化
linux·c语言·arm开发·c++·ubuntu
橘颂TA14 小时前
【测试】高效浏览器操作:基础功能与优化设置大全
c++·功能测试·职场和发展·测试·web测试
一只小小的芙厨14 小时前
寒假集训笔记·以点为对象的树形DP
c++·算法
艾莉丝努力练剑15 小时前
hixl vs NCCL:昇腾生态通信库的独特优势分析
运维·c++·人工智能·cann