This book belongs to “want to improve the book must read”, unfortunately, I suggest all C++ programmers have a look, nothing can also take out to read. You may also refer to the notes below to see if you understand all the terms and conditions.
I have uploaded a mind map of this note and a useful snippet of code to my GitHub. Please download it.
Get used to C++
- Think of C++ as a language federation
- C (part of the basic C language of C++)
- Object-oriented C++ (object-oriented)
- Template C++ (generic programming)
- STL (Standard Library)
- The code of efficient C++ programming varies, depending on which part of C++ you use.
Try to replace #define with const, enum, inline
- For simple constants, it is best to replace #define with const objects or enums.
- For function-like macros, it is best to replace #define with an inline function.
Use const whenever possible
- Declaring something as const helps the compiler detect bad usage. Const can be applied to any scoped object, function parameter, function return type, or member function body.
- Compilers enforce Bitwise Constness, but you should use “conceptual constness” when writing programs.
- When const and non-const member functions have substantially equivalent implementations, having the non-const version call the const version avoids code duplication.
Determines that the object is initialized before being used
- Manually initialize built-in objects because C++ does not guarantee that they will be initialized.
- Constructors are better off using initial member columns than using assignment operations inside the constructor itself. Member variables listed in the initial value column should be in the same order as they were declared in class.
- To avoid the “initialization order across compilation units” problem, replace a non-local static object with a local static object
Know which functions C++ silently writes and calls
- The compiler can secretly create the default constructor, copy constructor, copy Assignment operator, and destructor for class.
If you don’t want to use a function automatically generated by the compiler, you should explicitly reject it
- To override the functionality automatically provided by the compiler, the corresponding member function can be declared private and unimplemented. Another way to do this is to use a base class like Uncopyable.
Declare a virtual destructor for polymorphic base classes
- A virtual destructor is declared for a class only if it contains at least one virtual function.
- Declare a Pure Virtual destructor for the class you want it to be abstract, and it must be defined.
Do not let exceptions escape the destructor
- Destructors should never throw exceptions. If a function called by a destructor might throw an exception, the destructor should catch any exceptions and swallow them or end the program.
- If a customer needs to react to an exception thrown during the execution of an operation function, class should provide a normal function to perform the operation.
Never call virtual functions during construction and destruction
- Because during base Class construction, a virtual function is not a virtual function.
Let operator= return a reference to *this
Handle “self-assignment” in operator=
- Make sure operator= behaves well when the object self-assigns. Techniques include comparing the addresses of “source objects” and “target objects,” careful statement ordering, and copy-and-swap.
- Determines that any function that operates on more than one object, of which more than one object is the same object, still behaves correctly.
Remember every component of an object when copying it
- Copying functions should ensure that “all member variables within the object” and “all Base class components” are copied.
- Do not attempt to implement one copying function with another. One should put in a third function and call it jointly by two copying functions.
Manage resources by object
- shared_ptr
- unique_ptr
Be careful about copying behaviors in resource management classes
- Copying RAII objects must also copy the resources it manages, so the copying behavior of resources determines the copying behavior of RAII objects.
- One common and common behavior of RAII class copying is to inhibit copying and implement reference counting. But other behaviors can be implemented as well.
Provide access to raw resources in the Resource management class
- APIs often require access to raw resources, so each RAII class should provide a way to “get to the resources it manages.”
- Access to raw resources may be through explicit or implicit transformations. In general, explicit conversion is more secure, but implicit conversion is more convenient for customers.
Use the same form for new and DELETE in pairs
Place a NEwed object in a smart pointer with a stand-alone statement
- Failure to do so can result in undetected resource leaks once exceptions are thrown.
Make the interface easy to use correctly, not easy to misuse
- A good interface is easy to use correctly, not easy to misuse. You should strive for these properties in all of your interfaces.
- The approach of “promoting proper use” includes interface consistency and compatibility with the behavior of built-in types.
- Ways to “prevent misuse” include creating new types, limiting operations on types, constraining object values, and removing resource management responsibilities from the customer.
Designing a class is like designing a type
It is better to replace pass-by-value with pass-by-reference-to-const
- Try to replace pass-by-value with pass-by-reference-to-const. The former is usually more efficient and avoids cutting problems.
- The above rules do not apply to built-in types, as well as to STL iterators and function objects. For them, pass-by-value is often appropriate.
When you must return an object, do not attempt to return its reference
- • Never return pointer or Reference to a Local Stack object or return reference to a heap-Allocated object. Or return a pointer or reference to a local static object that may need both (except for singletons).
Declare member variables private
- Remember to declare member variables private. This gives customers consistent access to data, allows access control to be finely partitioned, allows constraints to be secured, and provides class authors with full implementation flexibility.
- Protect is no more encapsulated than Public.
Rather replace member functions with non-member and non-friend
- Having all convenience functions in multiple header files but in the same namespace means that customers can easily extend this set of convenience functions. All they need to do is add more non-member non-friend functions to the namespace.
- Doing so increases encapsulation, package elasticity, and skill expansion.
If all parameters require type conversions, use non-member functions to do so
Consider writing a swap function that does not throw exceptions
- When STD ::swap is not efficient for your type, provide a swap member function and make sure it does not throw an exception.
- If you provide a member swap, you should also provide a non-member swap to call the former. For classes (rather than templates), also specialize STD ::swap.
Delay the occurrence of variable definitions as long as possible
- Not only should you defer the definition of a variable until the moment you have to use it, you should even try to defer the definition until you can give it an initial argument.
Do transitions as little as possible
- If you can, avoid transitions, especially dynamic_casts in efficient code. If a design requires transformational action, try to develop an alternative design that does not require transformational action.
- If 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.
- Prefer C++_style (new) transitions to old ones. The former are easy to identify and have more distinct jobs.
Avoid returning handles to the internal components of the object
- Avoid returning handles (including references, Pointers, iterators) to the inside of the object. Adherence to this clause increases encapsulation, helps const member functions behave like const and minimizes the chance of dangling handles.
Striving for “exceptional safety” is worth it
- Exception-safe functions do not leak resources or allow any data structures to corrupt even if an exception occurs. Such functions are divided into three possible guarantees: basic, strong, and no exception.
- A “strong guarantee” can often be implemented as copy-and-swap, but a “strong guarantee” is not achievable or practical for all functions.
- The “exception security guarantee” provided by a function is usually only equal to the weakest of the “exception security guarantees” for each function it calls.
Understand the ins and outs of inlining
- On average, a program spends 80% of its execution time on 20% of its code. As a software developer, your goal is to find 20% of the code that actually increases your app’s overall sales, then inlining it or making it as thin as you can.
- Limit most inlining to small, frequently called functions. This makes future debugging and binary upgrades easier, minimizes potential code bloat problems, and maximizes the speed boost opportunities for your program.
- Don’t declare function templates inline just because they appear in header files.
Minimize compile dependencies between files
- One half of the idea behind compiler dependency minimization is dependency versus declarative, not dependency versus definition. The two approaches based on this idea are Handle Classes and Interface classes.
- Library header files should exist in full and declarative form only. This applies whether templates are involved or not.
Make sure your public inheritance models the IS-A relationship
- “Public Inheritance” means IS-A. Everything that is true of Base classes must be true of Derived classes, because every Derived Class object is also a Base class object.
Avoid obscuring inherited names
- Names in Derived classes obscure names in base classes. No one ever wanted that under public inheritance.
- To allow masked names to see the light of day, use a using declaration or corner function.
Distinguish between interface inheritance and implementation inheritance
- The Pure Virtual function only specifies interface inheritance.
- The plain (impure) Impure Virtual function specifies interface inheritance and default implementation inheritance.
- Non-virtual functions specify interface inheritance as well as mandatory implementation inheritance.
Consider alternatives to virtual functions
- Use the non-virtual interface (NVI) approach
- Replace virtual functions with “function pointer member variables”
- Replace the virtual function with the tr1::function member variable
- Replace a virtual function in an inheritance system with a virtual function in another inheritance system
Never redefine inherited non-virtual functions
Never redefine inherited default parameter values
- Because the default parameter values are statically bound, the virtual function — the only thing you should override — is dynamically bound.
To mold HAS-A or “out of something” by composite molding
- Composition means something completely different than public inheritance.
- In the application domain, composition means HAS-A (there is one). In the implementation domain. Compound means is-implemented- terms-of.
Use private inheritance wisely and judiciously
- Private inheritance means is-implemented in-terms of something. It is usually at a lower level than composition. But when a Derived class needs to access a protected Base class member, or when you need to redefine an inherited Virtual function, it makes sense.
- Unlike composition, private inheritance can lead to empty Base optimization. This can be important for library developers working on “object size minimization.”
Use multiple inheritance wisely and judiciously
- Multiple inheritance is more complex than single inheritance, which can lead to new ambiguities and the need for virtual inheritance.
- Virtual inheritance increases the cost of size, speed, initialization (and assignment) complexity, and so on. Zombies are most useful if virtual base classes don’t carry any data.
- Multiple inheritance does have legitimate uses. One scenario is a combination of “public inherits an Interface class” and “private inherits an assisted class.”
Learn about implicit interfaces and compiler polymorphism
- For the template argument, the interface is implicit, based on a valid expression. Polymorphism occurs in the compiler through template data representation and function overload resolution.
Understand the double meaning of typename
- The prefixes class and typename are interchangeable when declaring the template argument.
- Use the keyword typename to identify the nested subordinate typename: it must not be used as a base class modifier in base class lists (base class columns) or member initialization list (member initialization list).
Learn how to handle names within templated base classes
- Derived Class Templates can be done by referring to the member name in the Base class template with “this->”, or by an explicit “Base Class qualification modifier”.
Extract templates from code that is not dependent on parameters
- Templates generates multiple classes and multiple functions, so any template code should depend on some template parameter that causes inflation.
- Code bloat caused by type template arguments can often be eliminated by replacing template arguments with function arguments or class member variables.
- Code bloat due to type parameters can often be reduced by having the implementation code shared by existing types with exactly the same binary representation.
Accept all compatible types using a member function template
- Use the Member Function Template to generate functions that accept all compatible types.
- If you declare member Templates for “generalizing copy constructs” or “generalizing Assignment operations,” you still need to declare the normal copy constructor and copy Assignment operator.
Define nonmember functions for the template when type conversions are required
- When we write a class template that provides “related” functions that support “invisible type conversions of all parameters,” define those functions as “friend functions inside the class template.”
Use trait classes to represent type information
- Traits classes make type-specific information available at compile time. They are implemented with templates and “Templates specialization”.
- Incorporating overloading techniques makes it possible for trait classes to perform if… on types at compile time. The else test.
Learn about template metaprogramming
- Template Metaprogramming (TMP, Template metaprogramming) moves work from run time to compile time, enabling early error detection and higher execution efficiency.
- TMP can be used to generate custom code based on a combination of policy choices. It can also be used to avoid generating code that is not appropriate for particular types.
Understand the behavior of new-handler
- Set_new_handler allows a client to specify a function to be called when memory allocation cannot be satisfied.
- Nothrow New is a rather limited tool because it is only suitable for memory allocation; Subsequent constructor calls may still throw exceptions.
Know when new and DELETE should be replaced
- There are many reasons to write custom new and DELETE, including improving performance, debugging heap usage errors, and collecting heap usage information.
Stick to conventions when writing new and DELETE
- The operator new should contain an infinite loop in which it tries to allocate memory, and if it can’t meet its memory requirements, it calls new-handler. It should also be capable of handling 0bytes requests. The class-exclusive version should handle “larger than correct size (error) applications.”
- Operator delete should do nothing when a null pointer is received. The class-exclusive version should also handle “larger than correct size (error) applications.”
Write placement delete as well as placement New
- When you write a placement operator new, make sure you also write the corresponding Placement operator delete. If you don’t, your program may experience subtle and intermittent memory leaks.
- When you declare placement New and placement DELETE, be sure not to inadvertently (unintentionally) obscure their normal versions.