effective c++ notes
Accustoming yourself to C++
2 prefer const
s, enum
s, and inline
s to #define
.
- One special case need to be noted is class-specific constants. check this question in stackOverflow
- difference between declaration and definition. Check this question in stackOverflow
3 Use const
whenever possible
- declaring an iterator const is like declaring a pointer const.
const std::vector<int>::iterator iter = ...
, here iter acts like aT* const
. If you want an iterator that points to something that can’t be modified, you want aconst_iterator
. - 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.
4 Make sure that objects are initialized before they’re used.
- For constructor of a class, prefer using initialization list to assignment inside the body of the constructor.
- order in which an object’s data is initialized: base classes are initialized before derived classes, and within a class, data members are initialized in the order in which they are declared.
- avoid initialization order problems across translation units by replacing non-local static objects with local static objects.
Constructors, destructors, and assignment operators
- To disallow functionality automatically provided by compilers, declare the corresponding member functions
private
and give no implementations. Using a base class likeUncopyable
is one way to do this. [For c++11, you could use= delete
.] - declare desturctors virtual in polymorphic base classes. If a class has any virtual functions, it should have a virtual destructor.
- prevent exceptions from leaving destructors. If function called in a destructor may throw, the destructor should catch any exceptions, then swallow them or terminate the program.
- don’t call virtual functions during construction or destruction.
- have assignment operators return a reference to
*this
- make sure operator= is well-behaved when an object is assigned to itself: comparing addresses of source and target objects, careful statement ordering, and copy-and-swap.
Resource Management
- To prevent resource leaks, use RAII(resource Acquisition Is Initialization) oejects that acquire resources in their constructors and release them in their destructors.( auot_ptr and tr1::shared_ptr)[ For c++11,
shared_ptr
andunique_ptr
.] - when you use [] in
new
, remember usedelete []
- store newwd objects in smart pointers in standalone statements. Failure to do this can lead to subtle resource leaks when exceptions are thrown.
- Prefer pass-by-reference-to-const over pass-by-value. It’s typically more efficient and it avoids the slicing problem. The rule doesn’t apply to built-in types and STL iterator and function object types.
- don’t try to return a reference when you must return an object.
- declare date members
private
;protected
is no more encapsulated thanpublic
- prefer non-member non-friend functions to member functions. Doing so increase encapsulation, packaging flexibility, and functional extensibility.
- declare non-member functions when type conversions should aply to all parameters. For example, define
operator*
for classRational
. - Swap
1. provide a `swap` member function when `std::swap` would be _inefficient_ for your type. Make sure your `swap` doesn’t throw exceptions
- if you offer a member
swap
, also offer a non-memberswap
that calls the member. For classes(not templates), specializestd::swap
(total template specialization). - when calling
swap
, employ ausing
delcaration forstd::swap
, then callswap
without namespace qualification. - If’s fine to totally specialize
std
templates for user-defined types, but never try to add something completely new tostd
.
- if you offer a member
Implementations
- postphone variable definitions as long as possible.
- avoid casts whenever practical, especially
dynamic_cast
s in performance-sensitive code. If a design requires casting, try to develop a cast-free alternative. - avoid returning handles (references, pointers, or iterators) to object internals. It increase encapsulation, helps
const
member functions actconst
, and minimizes the creation of dangling handles. - strive for exception-safe code
Exception-safe functions leak no resources and allow no data structures to become corrupted, even when exceptions are thrown. Such functions offer the basic, strong, or nothrow guarantees. - Limit most inlining to small, frequently called functions. This facilitates debugging and binary upgradability, minimizes potential code bloat, and maximizes the chances of greater program speed.
- The general idea behind minimizing compilation dependencies is to depend on declarations instead of definitions. Two approaches based on this idea are Handle classes and Interface classes.
* **forward declaration**, check the answers [1](http://stackoverflow.com/questions/553682/when-can-i-use-a-forward-declaration) [2](http://stackoverflow.com/questions/4757565/c-forward-declaration) in stackOverlfow.
Ihheritance and Object-Oriented Design
- public inheritance means “is-a”. Everything that applies to base classes must also apply to derived classes.
avoid hiding inherited names
1. for pubic inherit, if you inherit from a base class with overloaded functions and you want to redefine or override only some of them, you need to include a `using` declaration for each name you’d otherwise be hiding. If you don’t, some of the names you’d like to inherit will be hidden.(why? first check the function name, then the type of parameter. See _c++ primer_, chap 15.6)
- for private inherit, if you don’t want to inherit all of them, you could use inline forwarding functions instead of
using
declaration.
- for private inherit, if you don’t want to inherit all of them, you could use inline forwarding functions instead of
differentiate between inheritance of interface and inheritance of implementation
1. The purpose of declaring a **pure virtual function** is to have derived classes inherit a function _interface only_.
- The purpose of declaring a simple virtual function is to have derived classes inherit a function interface as well as a default implementation(sometimes maybe dangerous, could separate functions for providing interface and default implementation)
- The purpose of declaring a non-virtual function is to have derived classes inherit a function interface as well as a mandatory implemntation.
Consider alternatives to virtual functions
* use the **non-virtual interface idiom**(NVI idiom), a form of the _Template Method design pattern_ that wraps public non-virtual member functions around less accessible virtual functions. Details of NVI in [wiki](https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-Virtual_Interface)(noted: derived classes could redefining private virtual functions)
- replace virtual functions with function pointer data members, a stripped-down manifestation of the Strategy design patternwiki.
- Replace virtual function with tr1::function data members, thus allowing use of any callable entity with a signature compatible with what you need.This, too, is a form of the Strategy desing pattern.[For c++11, it’s std::function and std::bind]
- replace virtual functions in one hierarchy with virtual functions in another hierarchy. This is the conventional implementation of the Strategy design pattern.
never redefine an inherited non-virtual function. cause non-virtual functions are statically bound.
- never redefine an inherited default parameter value, because default parameter values are statically bound, while virtual functions- the only functions you should be overriding - are dynamically bound.
- Model ‘has-a’ or ‘is-implemented-in-terms-of’ through composition.
- private inheritance means is-implemented-in-terms-of. It’s usually inferior to composition, but it makes sense when a derived class needs to composition(mixture of public inheritance and containment), but it makes sense when a derived class needs access to protected base class members or needs to redefine inherited virtual functions. private inheritance can enable the empty base optimization(“empty” class never have non-static data members, but contain typedefs, enums, static data members, or non-virtual functions), which could minimize object sizes.
- Multiple inheritance can lead to new ambiguity issues and to the need for virtual inheritance. Virtual inheritance imposes costs in size, speed, and complexity of initialization and assignment. It’s most practical when virtual base classes have no data.
One scenario involves combining public inheritance from an Interface class with private inheritance from a class that helps with implementation.
Templates and Generic Programming
both classes and templates support interfaces and polymorphism
1. classes: interfaces are explicit and centered on function signatures. Polymorphism: runtime through virtual functions
- template parameters: interfaces are implicit and based on valid expression. Polymorphism: compilation through template instantiation and function overloading resolution.
when declaring template paramters,
class
andtypename
are interchangeable. usetypename
to identify nested dependent type names, except in base class lists or as a base class identifier in a member initialization list.- In derived class templates, refer to names in base class templates via a
this->
prefix , viausing
declarations, or via an explicit base class qualification. factor parameter-independent code out of templates: templates generate multiple classes and multiple functions, which may cause bloat. about non-type parameter, check this in stackOverflow
1. bloat due to non-type template parameters can often be eliminated by replacing template parameters with funciton parameters or class data members.
- bloat due to parameters can be reduced by sharing implementations for instantiation types with identical binary representations.
use member function templates to generate functions that accept all compatible types.(kind of similar to Covariance and Contravariance in scala) (could use private build-in pointer the control the conversion, thus make it “covariance”).
If you declare member templates for generalized copy construction or generalized assignment, you’ll still need to declare the normal copy constructor and copy assignment operator, too.- When writing a class template that offers functions related to the template that support implicit type conversions on all parameters(see list 8 in Resource Management part), define thoses functions as friends inside the class template.7. Traits classes make information about types available during compilation. They’re implemented using templates and template specializations. In conjunction with overloading, traits classes make it possible to perform compile-time if…else tests on types.(create a set of overloaded “worker” functions or function templates that differ in a traits parameter).
Template metaprogramming(TMP) could shift work from runtime to compile-time, thus enabling earlier error detection and higher runtime performance. One “hello word”-type example:
template<unsigned n>
struct Factorial{
enum{ value = n * Factorial<n-1>::value};
};
template<>
struct Factorial<0>{enum {value = 1};
}
Customizing new
and delete
understand the behavior of the **new-handler
1. well-designed new-handler function must do one of the following: * Make more memory available * install a different new-handler * deinstall the new-handler * throw an exception * not return(abort or exit) 2. `set_new_hadler` allows you to specify a function to be called when memory allocation requests cannot be satisfied.
- Nothrow new is of limited utility, because it applies only to memory allocation. subsequent constructor calls may still throw exceptions.
few reasons to write custom versions of
new
anddelete
.* to detect usage errors
- to collect statistics about the use of dynamically allocated memory
- to increase the speed of allocation and deallocation
- to reduce the space overhead of default memory management
- to compensate for suboptimal alignment in the default allocator
- to cluster related objects near one another
- to obtain unconventional behavior.
operator
new
should contain an infinite loop trying to allocate memory, should call the new-handler if it can’t satisfy a memory request, and should handle requests for zero bytes. Class-specific versions should handle requests for larger blocks than expected(use standard operator new).
operatordelete
should do nothing if passed a pointer that is null. Class-specific versions should handle blocks that are larger than expected.(use standard operator delete)- when you are write a placement version of operator new, be sure to write the corresponding placement version of operator delete. If you don’t, your program may experience subtle, intermittent memory leaks.
When you declare placement versions ofnew
anddelete
, be sure not to unintentionally hide the normal versions of those functions.
Miscellany
- pay attention to compiler warnings, understand exactly what it’s trying to tell you.
- Familiarize yourself with Boost.