C++ – Effective C++

Contents

Item 01: Treat C++ as a language federation

C++ is divided into: C part, object C++ part, template C++ part, and STL part.

Item 02: Try to replace #define with const, enum, inline

const: Indicates that the modified content cannot be changed.

enum: The essence is int type.

inline: When inside a class, if the function is allowed, it is automatically enabled.

Item 03: Use const whenever possible

This helps the compiler better optimize the program and allows customers to reduce misoperations.

When passing values ​​to functions, it is best to use const references for custom types.

Modifying a member variable with mutable means that the member's const method is allowed to change the variable.

To avoid code duplication, call const functions with non-const functions instead of the other way around.

Item 04: Make sure the object is initialized before being used

Explicitly write out all member functions in the constructor table of the class, in the order of declaration.

The initialization order of member variables is the same as the declaration order.

If variables are used to declare global resources:

class FileSystem {
    // TODO: ...
};
extern FileSystem tfs;

In order to ensure the correct construction order, please put it in the factory function:

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

Item 05: Understand C++ and write and call functions silently

class Empty {
pubilc:
    Empty() { ... }
    Empty(const Empty & rhs) { ... }
    Empty(const Empty && rhs) { ... }
    ~Empty() { ... }

    Empty & operator = (const Empty & rhs) { ... }  
    Empty & operator = (const Empty && rhs) { ... }
};

The above are all default functions, it is best to write them all yourself.

Item 06: If you don’t want to use compiler-generated functions, you should explicitly reject them

As mentioned, use =delete after the function to delete the default function.

Item 07: Declare virtual destructors for polymorphic base classes

Deleting a derived class object using a pointer to a base class that has a non-virtual destructor results in undefined behavior. To correct this situation, the base class should be defined with a virtual destructor.

Making the base class destructor virtual ensures that objects of the derived class are destructed correctly.

Any time you have a virtual function in a class, you should immediately add a virtual destructor.

Item 08: Don’t let exceptions escape destructors

The destructuring function cannot throw exceptions. If there must be an exception, the program must be terminated directly.

Or put its exception part in another function, allowing users to call it in other ways in advance.

Item 09: Never call virtual functions during construction and destruction

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

Item 11: Handling "self-assignment" in operator =

First make a copy of the rhs data and then swap the copies.

Item 12: Don’t forget every component when copying an object

Call the copy function of the base class in the copy function when constructing copy.

Don't let one of the two copy functions call the other just because there is duplicate code. It should be placed in the third function.

Item 13: Managing resources with objects

Resources refer to things like GLFW windows. After creation, they need to be deleted using a specific function.

If you don't use object management, your clients may forget to delete.

std::auto_ptr: smart pointer, its copy function will set the copying party to nullptr.

Multiple objects are not allowed to hold the same pointer.

std::tr1::shared_ptr: Same as above, but allows a pointer to be held by multiple objects.

It destroys the pointer when the last object is destroyed, allowing a specific delete function to be specified.

Item 14: Be careful about copying behavior in resource management classes

This may result in the resource being destroyed while in use.

Item 15: Provide access to raw data in resource management classes

Returns a pointer to the original data.

Item 16: Use new and delete in the same form when paired

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

Prevent memory leaks when exceptions occur.

Item 18: Make the interface easy to use correctly and difficult to misuse

Make the class behave as closely as possible to int and STL.

Item 19: Designing a class is like designing a type

  • How to create and destroy objects of new type
  • What is the difference between object initialization and assignment?
  • What happens if type is passed by value
  • What are the legal values ​​of type
  • Do I need to match a certain inheritance chart system?
  • What kind of conversion is required for type
  • What operators and functions should be legal?
  • Which compiler-generated functions need to be rejected
  • Who should be a member of the new type
  • What is the "undeclared interface" of the new type?
  • Do you need templating?

Item 20: Prefer const passing by reference instead of passing by value

Item 21: When an object must be returned, don’t try to return a reference to it

Use the constructor directly in the return statement to create a new object.

Item 23: Prefer member functions to non-member or non-friend functions

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

Overloading operator using member functions when, if The left end of may require conversion, please use non-member functions.

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

For example, in the pimpl method only pointers need to be exchanged.

It is best to specialize std::swap to make it easier for customers to use.

If partial specialization is not possible (such as template class), do not consider adding overloaded functions to std.

It is a good idea to place special swap functions in the class namespace.

Item 26: The occurrence of variable definitions should be delayed as much as possible

Item 27: Do as little transformation as possible

C is converted to type name + variable name such as: (int)x or int(x).

New transformation solutions provided by C++:

const_cast: can remove constness.

dynamic_cast: Ensure safe first-downcasting. (May cause more resource consumption)

reinterpret_cast: low-level conversion, such as converting an int pointer to int.

static_cast: forced implicit transformation, roughly the same as C transformation.

Use less C casts!

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

handles: Things like non-const references and pointers that can change internal data.

Item 29: It’s worth the effort to be “exceptionally safe”

Basic promise: After an exception is thrown, everything in the program remains valid.

However, it is impossible to determine whether the effective state is the state before the function is executed, or whether it is restored to the default default state.

Strong guarantee: if the function fails, the program returns to the function call.

No exception guarantee: the function will not fail, decorated with noexcept. Or the failure is fatal to the program.

A strong guarantee can be achieved by processing a copy first and then swap it with the original data.

Item 30: Understand the ins and outs of inlining

Functions defined in class declarations are often inline.

The empty function in the class is not necessarily empty, so do not use inline in this case.

(He may have called functions of the parent class and members)

Item 31: Minimize compilation dependencies between files

Try to use forward class declarations.

This will lead to member variable problems, which can be solved with pimpl idiom.

If possible, try to use references and pointers instead of object bodies.

If possible, try to use class declarations instead of class definitions.

Create separate header files for declarative and definitional statements. (With fwd as the header file suffix)

Item 32: Make sure your public inheritance models an is-a relationship

That is, every subclass is also a base class. (Every student (subclass) is a person (base class))

You need to think, is a square a rectangle? What will happen if the height-changing property of the rectangle is inherited from the square?

Item 33: Avoid obscuring inherited names

Masking means that a variable within a scope can shield a global (outer scope) variable with the same name.

The functions of the subclass will overshadow the functions of the parent class.

If you want to inherit a parent class function and just want to overload it, use a using reference in the subclass.

Item 34: Distinguish between interface inheritance and implementation inheritance

The member function interface must be inherited, and whatever the parent class can do, the subclass must also be able to do.

It is possible to create a definition for a pure virtual function, but the scope domain name needs to be clearly marked when calling it.

Pure virtual function inheritance definition.

Virtual function inheritance definition and default implementation.

Non-virtual function inheritance definition and mandatory implementation.

Item 35: Consider alternatives to virtual functions

Template Method mode of Non-Virtual Interface: Let a non-virtual member function call a virtual internal virtual function. The method performs pre-processing before calling the virtual function, such as mutex locks, etc.

Strategy mode of Function Pointer: Similar to GLFW's callback function, binding is used to bind the function in the function constructor.

Strategy mode of tr1::function: tr1::function is automatically compatible with function pointers.

Traditional Strategy pattern: Put virtual functions into another inheritance system.

Item 36: Never redefine inherited non-virtual functions

Item 37: Never redefine inherited default parameter values

However, the default parameter value is static binding, so it is best to let the static function call the internal virtual function to achieve dynamic binding.

Item 38: To shape has-a or “to make out of something” by compounding

Compound: Put a class within a class.

The relationship between members and class is usually has-a (has one).

Implementing code based on something that would normally be used to use another class, but not an is-a relationship.

Item 39: Use private inheritance wisely and judiciously

Private inheritance does not have an inheritance relationship, that is, it is not an is-a relationship.

He realizes the relationship based on something.

Private inheritance means that public members in the parent class will become private.

Usually used for interfaces. Private inheritance can be replaced by composition.

EBO: An empty class will also occupy space if used as a member, but inheritance will not.

(The empty class is not really empty, it usually has static objects)

Item 40: Use multiple inheritance wisely and judiciously

If there is a base class member with the same name in multiple inheritance, the space needs to be clearly stated.

virtual public: Virtual inheritance will reduce program efficiency, but it can avoid inheriting a class multiple times.

Multiple inheritance is often used for functionality similar to Java interfaces.

Item 41: Understand implicit interfaces and compile-time polymorphism

Different templates are determined at compile time which functions they call.

The interface of a class is explicit and can be seen in the definition, but the template is not.

Item 42: Understand the double meaning of typename

template<class T> class Widget;
template<typename T> class Widget;

There is no difference between the above two expressions, but it is best to use class.

typename C::const_iterator it;

typename can be used to determine that the expression following it is a type, not a function, etc.

typename must be used as a prefix for nested dependent types and cannot exist in base class table columns and initialization table columns.

Item 43: Learn to handle names within templated base classes

Because a template base class can be specialized, its members cannot be used even if you inherit from the base class.

But it can be assumed that he does by defining qualification modifiers. Or use this pointer.

Item 44: Extract parameter-independent code out of templates

Using templates can lead to binary code bloat.

Use class members instead of template parameters to eliminate bloat.

Item 45: Use member function templates to accept all compatible types

Make a generalized copy function for the template class.

Even if you make a generalized copy function, your program will still use the default copy function.

Item 46: Define non-member functions for templates when type conversion is required

Defined as a friend function inside the class template.

Item 47: Use traits classes to represent type information

This technology is used to determine whether the type is legal (such as whether it is a built-in type) and give a warning at compile time.

Iterator:

Input: can only move forward and can only be read once.

output: can only move forward and write only once.

forward: can move forward and backward, can be read and written, and may be one-way in some implementations.

bidirectional: can move forward and backward, can be read and written, such as set.

Random access: can be any length forward or backward, can be read and written. For example vector.

traits classes:

Overloaded functions: The difference is only the traits parameters, which are used to distinguish between calling different functions.

Control function: Call the overloaded function.

Item 48: Understand template metaprogramming

Template metaprogramming is a program performed at compile time.

Here is an example of TMP, which can obtain factorials at compile time:

template<unsigned int n>
struct Factorial {
    enum { value = n * Factorial<n - 1>::value };
};
template<>
struct Factorial<0> {
    enum { value = 1 };
};

Item 49: Understand the behavior of new-handler

new-handler is a prompt function called when insufficient space is found during new.

It does not accept any parameters nor return any parameters.

You can use the set_new_handler function to set a custom function, which returns the previous new-handler function.

Item 50: Know when to replace new and delete appropriately

  • Used to detect application errors.
  • To collect usage statistics.
  • Increase running speed.
  • Reduce space usage.
  • Compensate for non-optimal alignment of the default allocator.
  • Place objects in clusters.
  • To obtain unconventional behavior.

Item 51: Stick to convention when writing new and delete

  • Handles 0-byte requests. (Consider this a 1-byte application)
  • Make sure there are no memory leaks.
  • When specially made for a class, if the requested size does not match, the default function is automatically called.
  • There should be a while(true) in new
  • Handle nullptr properly

Item 52: If you write placement new, you should also write placement delete.

These functions are used to allocate memory at specific locations.

Please do not unintentionally obscure the normal version of the allocator.

Item 53: Don’t take compile-time warnings lightly

Item 54: Familiarize yourself with the standard library, including TR1

TR1 itself is just a specification, which mainly implements Boost.

Component instance:

  • Smart pointers – tr1::shared_ptr and tr1::weak_ptr: the latter can handle rings.
  • tr1::function: represents all callable bodies, similar to function pointers.
  • tr1::bind: STL binder.

Independent functional part:

  • Hash tables: used to implement containers.
  • Regular expression.
  • Tuples: variable group, similar to pair, but can hold more variables.
  • tr1::array: array.
  • tr1::mem_fn: member function pointer.
  • tr1::reference_wrapper: Makes references behave more like objects.
  • random number.
  • Math library.
  • C99 extension.

Template programming part:

  • Type traits: used to determine whether it is a legal type.
  • tr1::result_of: used to deduce the function return value type.

Item 55: Familiarize yourself with Boost

boost.org

Post Reply