Effective C++ notes (on)

Guide

The purpose of this book is to learn how to use C++ effectively. The content is mainly divided into two categories: general design strategies and specific language features with specific details.

Article 1: Treat C++ as a language federation

C++ is mainly composed of C, object-oriented C++, Template C++, and STL. C++ efficient programming rules vary depending on the situation, depending on which part of C++ you use.

Article 2: Try to replace #define with const, enum, and inline

  1. For simple constants, it is best to replace #define with const objects or enums, because the constant names of #define will not enter the symbol table, which is not conducive to debugging;
  2. For macros that resemble functions, it is best to replace #defines with inline functions. The disadvantage of macros is that they are not type-safe and cannot be type-checked safely.

Item 3: Use const whenever possible

  1. Declaring something as const can help the compiler detect incorrect usage. const can be applied to objects, function parameters, function return types, and member function bodies in any scope.
  2. The compiler enforces bitwise constness, but the program you write should use "conceptual constness."
  3. When const and non-const member functions have substantially equivalent implementations, making the non-const version call the const version can avoid code duplication.

Item 4: Make sure that the object has been initialized before being used

  1. Manually initialize built-in objects, because C++ does not guarantee to initialize them;
  2. It is best to use member initial value columns in the constructor, rather than use assignment operations in the body of the constructor. The member variables listed in the initial value column should be arranged in the same order as their declaration order in the class.
  3. To avoid the problem of "initialization order across compilation units", please replace non-local static objects with local static objects.

Chapter 2 Construction, Destruction, and Assignment Operations

The classes we write will have at least a constructor, a destructor, a copy constructor, and a copy assignment operator. If we don't write it ourselves, the compiler will automatically create a default for us.

Item 5: Know which functions are written and called in C++ silently

The compiler can secretly create a default constructor, a copy constructor, a copy assignment operator, and a destructor for the class.

Item 6: If you don’t want to use the function automatically generated by the compiler, you should explicitly reject it

In order to reject the function automatically provided by the compiler, the corresponding member function can be declared as private and not implemented. Use a base class like uncopyable to encapsulate functions that you don't want the compiler to automatically generate into a class, and make other classes inherit.

Item 7: Declare virtual destructors for polymorphic base classes

  1. Polymorphic (with polymorphic nature) base classes should declare a virtual destructor. If the class has any virtual functions, it should have a virtual destructor.
  2. If the design purpose of classes is not to be used as base classes, or to have polymorphism (polymorphically), virtual destructors should not be declared.

Item 8: Don't let exceptions escape the destructor

  1. The destructor must never spit out an exception. If a function called by the destructor may throw an exception, the destructor should catch any exceptions and then swallow them or end the program.
  2. If the customer needs to react to an exception thrown during the operation of a certain operation function, then the class should provide an ordinary function (rather than in the destructor) to perform the operation.

Item 9: Never call virtual functions during construction and destruction

In the process of construction and destruction, the type of the object is base class. If the virtual function is called, dynamic binding will not occur. (The order of calling the constructors of the subclass and the parent class is the parent first and then the child, and the order of destructuring is reversed)

Item 10: Let operator= return a reference to *this

Item 11: Deal with "self-assignment" in operator=

  1. Make sure that the object behaves well when it self-assigns. Techniques for dealing with self-assignment include "identity test", careful and thoughtful sentence sequence, and cpoy-and-swap technology.
  2. Determine if any function operates on more than one object (such as array), and multiple objects (array[i] and array[j]) are the same object, its behavior is still correct.

Article 12: Don’t forget every component when copying objects

  1. The copying function should ensure that "all member variables in the object" and "all base class components" are copied.
  2. Don't try to implement another copying function with one copying function. The common function should be put into the third function and called by the two copying functions. (Copying function: copy constructor and copy assignment operator)

Chapter III Resource Management

The resources that need to be managed in C++ include dynamic allocation of memory, file descriptors, mutexes, fonts and brushes in the graphical interface, database connections, and network sockets.

Article 13: Manage resources by objects

  1. To prevent resource leaks, please use RAII objects (resources are obtained when the object is constructed and released during destructor), they obtain resources in the constructor and release resources in the destructor.
  2. Two commonly used RAII classes are tr1::shared_ptr and auto_ptr. The former is usually the better choice because its copy behavior is more intuitive. If auto_ptr is selected, the copy action will make it (the copied one) point to NULL.

Item 14: Be careful of copying behavior in resource management

  1. Copying RAII objects must also copy the resources it manages, so the copying behavior of resources determines the copying behavior of RAII objects.
  2. The common and common RAII class copying behaviors are: inhibit copying and use reference counting.

Item 15: Provide access to original resources in the resource management class

  1. APIs often require access to primitive resources (managed resources), so each RAII class should provide a method of "obtaining the resources it manages".
  2. Access to the original resource may be through explicit conversion or implicit conversion. Generally speaking, explicit conversion (a function that returns a managed resource) is safer, but implicit conversion is more convenient for customers.

Article 16: Use the same form when using new and delete in pairs

Well, new and delete must be used together, new matches delete, new[] matches delete[ ], and other forms of new and delete.

Item 17: Put the newed object into the smart pointer in a separate statement

The purpose of this is to ensure that the new object and passing it to the smart pointer are performed continuously, and to avoid memory leaks of the newed object due to other exceptions.

Chapter 4 Design and Declaration

Design guidelines for a good C++ interface.

Item 18: Make the interface easy to be used correctly and not easy to be misused

  1. The methods of "promoting correct use" include interface consistency and compatibility with built-in types of behavior.
  2. "Prevent misuse" methods include creating new types, restricting operations on types, constraining object values, and eliminating customer resource management responsibilities.
  3. tr1::shared_ptr supports customized deleters. This can prevent DLL problems, can be used to automatically release mutexes, etc.

Item 19: Designing a class is like designing a type?

The design of class is the design of type. Before defining a new type, please make sure you have considered all the discussion topics covered by this clause:

  1. How are objects created and destroyed?
  2. What is the difference between initialization and assignment?
  3. How to implement passed by value (copy constructor)?
  4. The "legal value" of the new type?
  5. Does it need to be inherited?
  6. How is the new type converted (explicit or implicit)?
  7. What operators and functions are needed for the new type?
  8. What standard functions need to be hidden?
  9. Which members are required for the new type?
  10. What is the "undeclared interface" of the new type?
  11. Is the new type general and does it need to be defined as a class template?
  12. If you define a derived class just to add some additional functions, you can use non-member functions or templates instead.

Item 20: Prefer pass-by-reference-to-const instead of pass-by-value

  1. Try to replace pass-by-value with pass-by-reference-to-const. The former is usually more efficient (avoid the operation of object copy), and can avoid the cutting problem (reference is actually implemented by pointers, which can achieve pointer polymorphism).
  2. The above rules do not apply to built-in types, and STL iterators and function objects. For them, pass-by-value is often more appropriate.

Item 21: When you must return an object, don’t try to return its reference

Never return a pointer or reference to a local stack object. Do not return a reference to a heap-allocated object. Do not return a pointer or reference to a local static object. Multiple such objects may be needed at the same time. Clause 4 has provided a design example for "returning a reference to a local static object reasonably in a single-threaded environment".

Item 22: Declare member variables as private

  1. Remember to declare member variables as private. This can give customers the consistency of access to data, can finely divide access control, promise constraint conditions to be guaranteed, and provide class authors with full implementation flexibility.
  2. Protected is not more encapsulated than public.

Item 23: Instead of non-member, non-friend instead of member function

Replacing member function with non-member non-friend function can increase encapsulation, wrapping flexibility, and performance scalability.
The so-called non-member non-friend refers to non-class member functions and friend functions, so how to define functions outside the class that can act on members of the class?
The answer is to define the function in the same namespace as the class, and then call the public member function of the class with the class object or its reference as a parameter.

Item 24: If all parameters require type conversion, use non-member functions for this purpose

If all the parameters of a function (including the metaphorical parameter pointed to by the this pointer) undergo type conversion, then the function must be a non-member.
Because implicit type conversion can only be applied to the parameters in the parameter list, if the metaphorical parameter pointed to by this pointer also needs to be type converted, it must be placed in the parameter column of the function, otherwise it cannot be implicitly converted of. When this function is declared as a member function, the metaphorical parameter pointed to by this pointer will not be included in the parameter list.

Item 25: Consider writing a swap function that does not throw an exception

  1. When std::swap is not efficient for your type, provide a swap member function and make sure that this function does not throw an exception.
  2. If you provide a member swap, you should also provide a non-member swap to call the former. For classes (not tempates), please also specialize std::swap.
  3. When calling swap, you should use the using declarative for std::swap, and then call swap without any "namespace qualification modification".
  4. Fully specializing std templates for "user-defined types" is good, but don't try to add something new to std in std.

Chapter 5 Implementation

Item 26: Delay the appearance of variable definitions as much as possible

Delay the appearance of variable definitions as much as possible to avoid the failure of constructors and destructors, avoid meaningless default construction behaviors, and prefer constructors with parameters ;
when the cost of assignment is lower than the cost of "construction + destruction", And when dealing with the highly sensitive part of the code, the constants used only in the loop body should be defined firstly outside the loop body , otherwise, the constants should be defined in the loop body first to avoid the constants causing conflicts with the code outside the loop body.

Item 27: Do as few transformations as possible

  1. If possible, try to avoid transformation, because it is easy to cause undefined behavior, especially avoid dynamic_casts in efficiency-conscious code (because this is the most efficient type of forced transformation). If there is a design that requires transformation, try to develop alternative designs that do not require transformation.
  2. If the transformation is necessary, try to hide it behind a function. Customers can then call this function without having to put the transformation into their own code. (Reduce code coupling)
  3. It is better to use C++_style transformation instead of old-style transformation. The former is easy to identify, and there are more categorized responsibilities. (These four new transformations can clearly show the type of transformation, and each transformation action has a single function)

Item 28: Avoid returning handles to the internal components of the object

Avoid returning handles (including references, pointers, iterators) to point to the inside of the object. Compliance with this clause can increase encapsulation, help const member functions behave like a const, and minimize the possibility of "virtual tag numbers (handles pointing to non-existent objects)".

Article 29: It is worthwhile to work for "abnormal safety"

  1. The exception safety function will not leak resources even if an exception occurs (available object management resources), nor will it allow any data structure to be destroyed (strong guarantee first).
  2. An exception safety function must provide one of three possible guarantees: basic guarantee, strong guarantee, and non-throwing guarantee . Strong guarantee is given first, followed by basic guarantee, and no-throw guarantee at the end.
  3. "Strong guarantee" can often be realized by copy-and-swap, but "strong guarantee" is not achievable or realistic for all functions.
  4. The "abnormal safety guarantee" provided by a function is usually the highest equal to the weakest of the "abnormal safety guarantees" of the functions it calls .

Article 30: Thoroughly understand the inside and out of inlining

  1. The advantage of the inline function: it can be optimized by the compiler without the extra overhead of function calls. Disadvantages: it will cause code bloat.
  2. Inline is only an application for the compiler, not a mandatory command. Whether the final function is inlining depends on the compiler; this application can be proposed metaphorically (define the function in the class) or display it (the function at the function definition) Add inline at the forefront of the name).
  3. Inline functions and templates are usually defined in header files, because most compilers are inlining during compilation, and the called inline functions need to be replaced with "function bodies" at this stage; templates also need to be implemented in the compilation stage. Now turned into a concrete function or class.
  4. Limit most inlining to small, frequently called functions. This can make the subsequent debugging process (breakpoints cannot be set in inline functions) and binary upgrades (inline functions cannot be upgraded with the upgrade of the program library) easier, and can also minimize potential code bloat problems and make the program faster Maximize opportunities for improvement.
  5. Do not declare function templates as inline just because they appear in the header file.
  6. The strategy for declaring inline functions: Don't declare any function as inline at the beginning, or at least limit the scope of inlining to those functions that "must become inline" or "very plain".

Clause 31: Minimize compilation dependencies between files (Didn't understand

  1. The general idea that supports "minimization of compilation dependency" is: depend on declarative, not on definitive. The two methods based on this idea are Handle classes and Interface classes.
  2. The library header file should exist in a "complete and only declarative" form. This approach applies regardless of whether templates are involved.

Guess you like

Origin blog.csdn.net/gaggagaasd/article/details/108851251