Effective C++ 50 Items
- Prefer const and inline to #define
- const
- in class
- static member
- enum trick
- macro
- use inline function instead
- use template together with the inline function
- Prefer <iostream> to <stdio.h>
- <iostream>: functions are in the std namespace.
- <iostream.h> functions are in the global scope.
- Prefer new and delete to malloc and free
- with malloc/free, constructors and destructors are not involved and this is not good.
- Prefer C++ style comments
- Use the same form in corresponding uses of new and delete
- pay attention to typedef, especially when the array is involved in the definition.
- Use delete on pointer members in destructors
- adding a pointer member almost always requires each of the following:
- initialization of the pointer in each of the constructor.
- deletion of the existing memory and assignment of new memory in the assignment operator.
- deletion of the pointer in the destructor.
- Be prepared for out-of-memory condition
- assertions are checked in debug mode when NDEBUG isn't defined.
- a well-defined new-handler function must do one of the following
- make more memory available
- one way to implement this strategy is to allocate a large block of memory at program start-up, then release it in the first time the new handler is invoked.
- install a different new-handler
- de-install the new-handler
- pass the null pointer to set_new_handler
- throw an exception
- not return
- call abort or exit
- Adhere to convention when writing operator new and operator delete
- operator new actually tries to allocate memory more than once.
- only when the pointer to the error-handling function is null does operator new throw an exception.
- operator new is inherited by subclasses.
- new T[5] calls operator new[](count) where count = sizeof(T) * 5 + overhead.
- because the operator new or operator new[] can be called by a subclass, essentially, we cannot know the exact size of the element, it can be either sizeof(base) or the sizeof(derived)
- Avoid hiding the "normal" form of new
- write a class-specific operator new that supports the normal invocation form
- an alternative is to provide a default parameter value for each additional parameter you add to operator new.
- write operator delete if you write operator new
- the main reason we want to overload the operator new and operator delete is performance.
- the default operator new/delete may need to handle the communication between these two operators and additional header is needed.
- we may need to have a virtual function in the class to make the customized operator delete work.
- Declare a copy constructor and an assignment operator for classes with dynamically allocated memory.
- write your own copy constructor and assignment operator if you have any pointers in your class.
- Prefer initialization to assignment in constructors
- List members in an initialization list in the order in which they are declared
- class members are initialized in the order of their declaration in the class.
- base class data members are initialized before derived class data members.
- Make sure base classes have virtual destructor
- you have a class that you'd like to be abstract, but you don't happen to have any functions that are pure virtual. In this situation, you can declare a pure virtual destructor.
- Have operator= return a reference to *this
- Assign to all data members in operator=
- be careful about the implementation in the derived class. Most of the time, we need to explicitly call the operator=, copy constructor of the base class.
- Check for assignment to self in operator=
- Strive for class interfaces that are complete and minimal
- Differentiate among member functions, non-member functions and friend functions
- The biggest difference between member functions and non-member functions is that member functions can be virtual and non-member functions cannot.
- virtual functions must be members
- operator>> and operator<< are never members.
- Only non-member functions get type conversions on their left-most argument.
- Everything else should be a member function.
- Avoid data members in the public interface.
- Use const whenever possible
- mutable keyword
- const member functions
- bitwise constness
- conceptual constness
- Prefer pass-by-reference to pass-by-value
- Don't try to return a reference when you must return an object
- Choose carefully between function overloading and parameter defaulting
- Avoid overloading on a pointer and a numerical type
- Think about the following example
- void f(int x);
- void f(string *ps);
- call f(0)
- member function template
- Guard against potential ambiguity
- Explicitly disallow use of implicitly generated member functions you don't want
- declare the function private
- do not implement the function
- Partition the global namespace
- Avoid returning "handlers" to internal data
- conversion of types
- Avoid member functions that return non-const pointers or reference to members less accessible than themselves.
- Never return a reference to a local object or to a dereference pointer initialized by new within the function.
- Postpone variable definitions as long as possible
- Use inlining judiciously
- The inline directive, like register, is a hint to compilers, not a command.
- Minimize compilation dependencies between files
- Avoid using objects when object references and pointers will do
- use class declarations instead of class definitions whenever you can.
- don't #include header files in your header files unless your headers won't compile without them
- Make sure public inheritance models "is-a"
- Differentiate between inheritance of interface and inheritance of implementation.
- the purpose of declaring a pure virtual function is to have derived classes inherit a function interface only.
- it is possible to provide a definition for a pure virtual function. But the only way to call it would be to fully specify the call with the class name.
- The purpose of declaring a simple virtual function is to have derived classes inherit a function interface as well as a default implementation.
- the purpose of declaring a non-virtual function is to have derived classes inherit a function interface as well as a mandatory implementation.
- Never redefine an inherited non virtual function
- Never redefine an inherited default parameter value
- virtual functions are dynamically bound but default parameter values are statically bound.
- the default parameters are determined during the compilation time. The reason for this behaviour is efficiency.
- Avoid casts down the inheritance hierarchy
- Model "has-a" or "is-implemented-in-term-of" through layering.
- Differentiate between inheritance and templates
- Try to answer the question: does the type T affect the behaviour of the class? If T does not affect the behaviour, you can use a template. If T does affect the behaviour, you'll need virtual functions and you'll therefore use inheritance.
- Use private inheritance judiciously
- In contrast to public inheritance, compilers will generally not convert a derived class object into a base class object if the inheritance relationship between the classes is private.
- Private inheritance means is-implemented-in-terms-of.
- Private inheritance is purely an implementation technique.
- Private inheritance means that implementation only should be inherited; interface should be ignored.
- Private inheritance means nothing during software design, only during software implementation.
- Use multiple inheritance judiciously
- Say what you mean; understand what you are saying
- A common base class means common traits.
- Public inheritance means "is-a".
- Private inheritance means "is-implemented-in-terms-of".
- Layering or composition means "has-a" or "is-implemented-in-terms-of"
- A pure virtual function means that only the function's interface is inherited.
- A simple virtual function means that the function's interface plus a default implementation is inherited.
- A non-virtual function means that the function's interface plus a mandatory implementation is inherited.
- Know what functions C++ silently writes and calls
- Prefer compile-time and link-time errors to runtime errors
- Ensure that non-local static objects are initialized before they're used
- singleton pattern
- the order of non-local static objects
- use function that returns the reference of a static variable
- Pay attention to compiler warnings
- Familiarize yourself with the standard library
- Improve your understanding of C++