<Effective C++>读书摘要--Accustoming Youself to C++

<Item 1>View C++ as a federation of languages。

1、把C++看成4种子语言组成,即C、Object-Oriented C++、Template C++、The STL。

2、Things to Remember:Rules for effective C++ programming vary, depending on the part of C++ you are using.  

因为C++有很多的编程范式,在项目开发过程中,明确规范怎么使用C++很重要,这样可以使整个团队尽量使用一样的风格,并把项目一直做下去

 <Item 2>Prefer consts, enums, and inlines to #defines。

3、prefer the compiler to the preprocessor。因为宏定义不会被编译到符号表里面去,因此会对问题的调试定位带来麻烦,同时宏定义不能进行面向对象的封装和私有化。

4、使用常量代替宏定义的时候需要注意两个点,

一个是定义指针常量的时候需要使用两个const,如

  const char * const authorName = "Scott Meyers"; 

其中前一个const修饰char,后一个const修饰指针,可以逆着读authorName 是一个const指针,指向const的char常量

另一个是使用class-specific constant的时候,为了保持一个内存copy,需要使用static进行定义

 class GamePlayer {
 private:
 static const int NumTurns = 5; // constant declaration
 int scores[NumTurns]; // use of constant
 ...
 };

What you see above is a declaration for NumTurns, not a definition. Usually, C++ requires that you provide a definition for anything you use, but class-specific constants that are static and of integral type (e.g., integers, char s, bool s) are an exception. As long as you don't take their address, you can declare them and use them without providing a definition. If you do take the address of a class constant, or if your compiler incorrectly insists on a definition even if you don't take the address, you provide a separate definition like this:

  const int GamePlayer::NumTurns; // definition of NumTurns; see 2 // below for why no value is given 

Because the initial value of class constants is provided where the constant is declared (e.g., NumTurns is initialized to 5 when it is declared), no initial value is permitted at the point of definition.(也可以声明的时候不给初值,而在定义的时候给初值)

Note, by the way, that there's no way to create a class-specific constant using a #define, because #define s don't respect scope.

5、the enum hack

 class GamePlayer {
 private:
 enum { NumTurns = 5 }; // "the enum hack" — makes
 // NumTurns a symbolic name for 5
 int scores[NumTurns]; // fine
 ...
 };

First, the enum hack behaves in some ways more like a #define than a const does, and sometimes that's what you want. For example, it's legal to take the address of a const, but it's not legal to take the address of an enum, and it's typically not legal to take the address of a #define, either. If you don't want to let people get a pointer or reference to one of your integral constants, an enum is a good way to enforce that constraint.

A second reason to know about the enum hack is purely pragmatic. Lots of code employs it, so you need to recognize it when you see it. In fact, the enum hack is a fundamental technique of template metaprogramming

6、another common (mis)use of the #define directive is using it to implement macros that look like functions but that don't incur the overhead of a function call.

  // call f with the maximum of a and b

 #define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b))

替换为

 template<typename T> // because we don't
 inline void callWithMax(const T&a,const T&b)// know what T is, we
 { // pass by reference-to-
 f(a > b ? a : b); // const — see Item 20
 }

7、Given the availability of const s, enum s, and inline s, your need for the preprocessor (especially #define) is reduced, but it's not eliminated. #include remains essential, and #ifdef /#ifndef continue to play important roles in controlling compilation. It's not yet time to retire the preprocessor, but you should definitely give it long and frequent vacations.

8、Things to Remember:

 For simple constants, prefer const objects or enums to #define s.
 For function-like macros, prefer inline functions to #define s.

<Item 3>Use const whenever possible。

9、const施加了一个semantic constraint,并有编译器来保证,同时It allows you to communicate to both compilers and other programmers that a value should remain invariant.

10、下面两种语法格式含义一致都表示Widget 为const常量

 void f1(const Widget *pw); // f1 takes a pointer to a
 // constant Widget object
 void f2(Widget const *pw); // so does f2

11、If you want an iterator that points to something that can't be modified (i.e., the STL analogue of a const T* pointer), you want a const_iterator :

 std::vector<int> vec;
 ...
 const std::vector<int>::iterator iter = // iter acts like a T* const
 vec.begin();
 *iter = 10; // OK, changes what iter points to
 ++iter; // error! iter is const
 std::vector<int>::const_iterator cIter = //cIter acts like a const T*
 vec.begin();
 *cIter = 10; // error! *cIter is const
 ++cIter; // fine, changes cIter

12、Having a function return a constant value often makes it possible to reduce the incidence of client errors without giving up safety or efficiency.

 class Rational {... };
 const Rational operator*(const Rational& lhs, const Rational& rhs);
 Rational a, b, c;
 ...
 (a * b) = c; // invoke operator= on the
 // result of a*b!
 if (a * b = c)... // oops, meant to do a comparison!

13、The purpose of const on member functions is to identify which member functions may be invoked on const objects. Such member functions are important for two reasons. First, they make the interface of a class easier to understand. It's important to know which functions may modify an object and which may not. Second, they make it possible to work with const objects. That's a critical aspect of writing efficient code, because, as Item 20 explains, one of the fundamental ways to improve a C++ program's performance is to pass objects by reference-to-const. That technique is viable only if there are const member functions with which to manipulate the resulting const -qualified objects.

14、Many people overlook the fact that member functions differing only in their constness can be overloaded, but this is an important feature of C++.const objects most often arise in real programs as a result of being passed by pointer- or reference-to-const.

15、What does it mean for a member function to be const ? There are two prevailing notions: bitwise constness (also known as physical constness) and logical constness.The bitwise const camp believes that a member function is const if and only if it doesn't modify any of the object's data members (excluding those that are static), i.e., if it doesn't modify any of the bits inside the object.In fact, bitwise constness is C++'s definition of constness, and a const member function isn't allowed to modify any of the non-static data members of the object on which it is invoked.(对象中包含指针的时候,指针不变但是指针指向的数据可变)。Adherents to this philosophy argue that a const member function might modify some of the bits in the object on which it's invoked, but only in ways that clients cannot detect.mutable frees non-static data members from the constraints of bitwise constness:

class CTextBlock {
public:
...
std::size_t length() const;
private:
char *pText;
mutable std::size_t textLength; // these data members may
mutable bool lengthIsValid; // always be modified, even in
}; // const member functions
std::size_t CTextBlock::length() const
{
if (!lengthIsValid) {
textLength = std::strlen(pText); // now fine
lengthIsValid = true; // also fine
}
return textLength;
}

16、Avoiding Duplication in const and Non-const Member Functions

class TextBlock {
public:
  ...
  const char& operator[](std::size_t position) const     // same as before

  {
    ...
    ...
    ...
    return text[position];
  }
  char& operator[](std::size_t position)         // now just calls const op[]
  {

    return  const_cast<char&>(                         // cast away const on
                                                 // op[]'s return type;
        static_cast<const TextBlock&>(*this)     // add const to *this's type;
          [position]                            // call const version of op[]
      );
  }
...
};

16、Things to Remember

 Declaring something const helps compilers detect usage errors. const can be applied to objects at any scope, to function parameters and return types, and to member functions as a whole.
 Compilers enforce bitwise constness, but you should program using conceptual constness.
 When const and non-const member functions have essentially identical implementations, code duplication can be avoided by having the non-const version call the const version.

<Item 4> Make sure that objects are initialized before they're used

17、The rules of C++ stipulate that data members of an object are initialized before the body of a constructor is entered.参数初始化列表才是真实的初始化过程,构造函数内部的赋值不是初始化。For objects of built-in type like numTimesConsulted, there is no difference in cost between initialization and assignment, but for consistency, it's often best to initialize everything via member initialization.

18、Sometimes the initialization list must be used, even for built-in types. For example, data members that are const or are references must be initialized; they can't be assigned

19、当有很多成员或者基类需要初始化的时候,it's not unreasonable to omit entries in the lists for data members where assignment works as well as true initialization, moving the assignments to a single (typically private) function that all the constructors call.

20、One aspect of C++ that isn't fickle is the order in which an object's data is initialized. This order is always the same: base classes are initialized before derived classes (see also Item 12), and within a class, data members are initialized in the order in which they are declared.  成员初始化的顺序和初始化列表中的顺序无关,但是建议初始化列表中的数据和声明的顺序一致,以免读者误解

21、A static object is one that exists from the time it's constructed until the end of the program. Stack and heap-based objects are thus excluded. Included are global objects, objects defined at namespace scope, objects declared static inside classes, objects declared static inside functions, and objects declared static at file scope. Static objects inside functions are known as local static objects (because they're local to a function), and the other kinds of static objects are known as non-local static objects. Static objects are automatically destroyed when the program exits, i.e., their destructors are automatically called when main finishes executing.

22、A translation unit is the source code giving rise to a single object file. It's basically a single source file, plus all of its #include files.

23、The problem we're concerned with, then, involves at least two separately compiled source files, each of which contains at least one non-local static object (i.e., an object that's global, at namespace scope, or static in a class or at file scope). And the actual problem is this: if initialization of a non-local static object in one translation unit uses a non-local static object in a different translation unit, the object it uses could be uninitialized, because the relative order of initialization of non-local static objects defined in different translation units is undefined.解决方案是使用函数封装static变量确保初始化。This approach is founded on C++'s guarantee that local static objects are initialized when the object's definition is first encountered during a call to that function.

class FileSystem { ... };           // as before
FileSystem& tfs()                   // this replaces the tfs object; it could be
{                                   // static in the FileSystem class
  static FileSystem fs;             // define and initialize a local static object
  return fs;                        // return a reference to it

}

class Directory { ... };            // as before
Directory::Directory( params )      // as before, except references to tfs are
{                                   // now to tfs()
  ...
  std::size_t disks = tfs().numDisks();  //使用函数封装确保初始化
  ...
}

Directory& tempDir()                // this replaces the tempDir object; it
{                                   // could be static in the Directory class
  static Directory td;              // define/initialize local static object
  return td;                        // return reference to it
}

 24、On the other hand, the fact that these functions contain static objects makes them problematic in multithreaded systems. Then again, any kind of non-const static object — local or non-local — is trouble waiting to happen in the presence of multiple threads. One way to deal with such trouble is to manually invoke all the reference-returning functions during the single-threaded startup portion of the program. This eliminates initialization-related race conditions.

25、Things to Remember
 Manually initialize objects of built-in type, because C++ only sometimes initializes them itself.
 In a constructor, prefer use of the member initialization list to assignment inside the body of the constructor. List data members in the initialization list in the same order they're declared in the class.
 Avoid initialization order problems across translation units by replacing non-local static objects with local static objects.

原文地址:https://www.cnblogs.com/lshs/p/4396938.html