【Effective C++ 笔记】让自己习惯C++

      最近在读 Effective C++,想要做点笔记,归类和书中的每个模块一样,但跟模块里的具体顺序可能不太一致。不会对书中每个细节都涉及,主要记下自己觉得重要的内容。


     What is C++?      

      C++ 是一个多重范型编程语言( multiparadigm programming language),一个同时支持过程形式(procedural)、面向对象形式(object-oriented)、函数形式(functional)、泛型形式(genetic)、元编程形式(metaprogramming)的语言。
     我们 可以视 C++ 为主要的四个次语言组成,包括:C、Object-Oriented C++、Template C++、STL。(觉得这个归类好有道理...废话...)


     const 那些事

     语法:如果关键字 const 出现在星号左边,表示被指物是常量;如果出现在星号右边,表示指针自身是常量;如果出现在星号两边,表示被指物和指针两者都是常量。

     STL 迭代器的作用就像个 T* 指针,声明迭代器为 const 就像声明指针为 const 一样(即声明一个 T* const 指针),表示这个迭代器不得指向不同的东西,但所指的东西的值是可以改动的。如果要迭代器所指的东西不可被改动(即希望 STL 模拟一个 const T* 指针),则要用 const_iterator.

     const vector<int>::iterator iter = vec.begin(); // 所指物可变,迭代器不可变
     vector<int>::const_iterator cIter = vec.begin(); // 所指物不可变,迭代器可变

    尽可能不要用 #define 定义常量,而是用 const 变量。

    定义常量指针时有必要将指针(而不只是指针所指之物)声明为 const。(成为指向 const 的 const 指针)

    class 专属常量应该使其成为 class 的一个 static 成员,这样既可以限制常量作用域,而且只有一个实体。

    令函数返回一个常量值,可以降低因客户错误造成的意外,又不至于放弃安全性和高效性。

    例如重载运算符 *,加入返回的不是 const, 可能会出现这样的错误。

    if (a*b = c) ...  // 将比较误写成了赋值

     两个成员函数如果只是常量性不同,可以被重载。例如:

     const char& operator[](size_t position) const { }
     char& operator[](size_t position){ }
      bitwise constness
     成员函数只有在不更改对象之任何成员变量时才可以说是 const。bitwise constness 正是 C++ 对常量性的定义。

     结果是很多成员函数不十足具备 const 性质,却能通过编译。例如 char* 属于对象而非其所指物,那么就可以通过成员函数返回的指针去修改其所指物,可以通过编译器检测。

     logical constness:

     一个成员函数可以修改它所处理的对象内的某些 bits,但只有在客户端侦测不出的情况下才行。

    要在 const 成员里修改数据,需要将变量声明为 mutable。mutable 释放掉 non-static 成员变量的 bitwise constness 约束。例如: mutable size_t textLength;

    编译器强制实施 bitwise constness,但你编写程序时应该使用“概念上的常量性”。


     const 与 非 const  的复用
     当 const 和 non-const 成员函数有着实质等价的实现时,令 non-const 版本调用 const 版本代码可避免重复。

     注意:const 成员函数调用 non-const 成员函数是一种错误行为。

class TextBlock{
     public:
          const char& operator[] (std::size_t position) const
          {
               ...;
               ...;
               return text[position];
          }
          char& operator[] (std::size_t position)
          {
               return const_cast<char&>(
                         static_cast<const TextBlock&>(*this)[position]
                         );    //要看懂这个转化
          }
}

    

     函数的 little tips 

    参数传递:

    对于内置(也就是 C-like)类型而言 pass-by-value 通常比 pass-by-reference 高效。在 Object-Oriented C++ 中,由于用户自定义构造函数和析构函数的存在, pass-by-reference-to-const 往往更好,Template C++ 也如是。而对 STL 的迭代器和函数对象而言,旧式的 C pass-by-value 更加适用。


    inline:用 inline 函数代替形似函数的宏,以避免不必要的错误。


     对象初始化

    为内置对象进行手工初始化,因为 C++ 不保证初始化它们。

     构造函数最好使用成员初始初值列(member initialization list),而不要在构造函数本体内使用赋值操作(assignment)。初值列列出的成员变量,其排列次序应该和它们在 class 中的声明次序相同。使用成员初值列有时候绝对必要,又往往比赋值更高效。

     class 的成员变量总是以其声明次序被初始化。为避免问题,你初始化的顺序应该和声明的顺序一样。


      为免除”跨编译单元之初始化次序“问题,请以 local static 对象替换 non-local static 对象。因为 C++ 对”定义于不同的编译单元内的 non-local static 对象“初始化相对顺序并无明确定义,要消除该问题,应该如下设计:将每个 non-local static 对象搬到自己的专属函数内(该对象在此函数内被声明为 static )。C++ 保证,函数内的 local static 对象会在”该函数被调用期间“”首次遇上该对象之定义式“时被初始化。

class FileSystem{};

FileSystem& tfs()
{
     static FileSystem fs;
     return fs;
}

class Directory{...};
Directory::Directory(params){
     ...;
     std::size_t disks = tfs().numDisks();
     ...;
}

Directory& tempDir()
{
     static Directory td;
     return td;
}
     然而,任何一种 non-const static 对象,不论它是 local 还是 non-local,在多线程的环境下”等待某事发生“都会有麻烦,处理这个麻烦的一个做法是:

     在程序的单线程启动阶段(single-threaded startup portion)手工调用所有 reference-returning 函数,这可消除与初始化有关的"竞速形势”(race condition)。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。