Item 55: Familiarize Yourself With Boost
Because Boost is cool and is a testbed of future C++ standard library.
Reading notes of Effective C++, 3rd ed.
Because Boost is cool and is a testbed of future C++ standard library.
Which becomes C++03. And now it’s C++11 and even C++14.
Or better yet, -Werror.
Any operator new overload that accepts no just std::size_t as parameter is called a “placement new” (yeah, the name “placement new” is overloaded, too). You must define a paired overloaded version of operator delete, or you are in serious trouble, because when compiler can’t find one, it just gives up deleting the memory when a constructor fails (leak!).
Here are the conventions (tricker than you thought):
Probably never - it’s very hard to write a conformant and performant memory allocator (for just the tip of the iceberg, a conformant allocator must return aligned memory addresses).
operator new will repeatedly(!) call new-handler until it can find enough space.
TMP is Turing-complete, and the funny thing is, it is discovered, not invented.
Traits: Conventions (not syntax rules) that allow you to get information about type during compilation.
Recall: Item 24 tells that you should define non-member function when type conversions are desired. In templatized version, you have to define non-member function inside a class.
Or: How to make smart pointers do type conversion like raw pointers? (Of course, the trick here doesn’t just apply to smart pointers.)
To reduce code size (when/if output binary size is an issue). (By the way, member functions are only instantiated only when used.)
Via this-> prefix, via using declaration, or via an explicit base class qualification.
Two use cases: To use in template declaration and to give compiler a hint that this name is a type.
Template: Implicit interface (whatever is a valid expression) and compile-time polymorphism (template instantiation and function overloading resolution).
When diamond inheritance is an issue, public inheritance should probably always be virtual inheritance. (But remember, virtual inheritance incurs extra costs - size, speed, and complexity of code you need to write.)
Private inheritance means “is-implemented-in-terms-of” (never “has-a” nor “is-a”).
We calls it “has-a” relationship in application domain, and “is-implemented-in-terms-of” relationship in implementation domain.
Because default parameter values are statically bound, even for virtual functions (surprise!).
Non-virtual functions are statically bound (no lookup of vptr table at runtime). So redefining it is almost never right (you are breaking the core principle that public inheritance models “is-a” relation).
Through NVI (non-virtual interface) idiom or the strategy pattern.
Consider: Pure virtual (with or without implementation), simple (impure) virtual, and non-virtual function.
Names in derived class hides names in base class (virtual won’t changes this, it only tells runtime to lookup vptr table), which is usually undesirable. To make hidden names visible again, employ using declarations or employ forwarding functions. (This is especially annoying when base class defines a group of overloaded functions.)
Note: Liskov substitution principle (LSP).
General idea: Depend on declaration, not definition. You might need to alter class design to accommodate this (see handle class pattern or interface class pattern).
inline is a request to compilers, not a command. Defining a function inside a class definition is an implicit request.
Exception safe means: Leak no resources and don’t allow data structure to become corrupted (i.e., object invariances must be preserved).
Handles include pointers, reference, and iterators (ways to get at other objects). It decreases encapsulation and might result in dangling pointer. You should only prefer using handles in certain specific scenarios (like containers).
Avoid casts whenever practical. There are 5 casts: 1 C-style and 4 C++ style casts.
So that you only allocate resource when you are about to use it.
(Or: How to define swap the right way so that the compiler may find the correct one.) There are four swaps to consider: the default std::swap, member swap, non-member swap in the same namespace, and specialized std::swap. You need to assist compiler find the best one to use. And all call sites needs to tell compiler that it should choose the best one (don’t hard-coded std::swap).
If you need type conversion on all parameters (including this), that function must be non-member.
To increase encapsulation.
To maintain class invariants. And protected is not more encapsulated than public.
You can’t return a reference to a local object, duh!
Because it’s (most likely) more efficient and avoids the slicing problem.
Because a new class is a new type.
Be consistent, and use type system as your primary ally in preventing undesirable code from compiling (i.e., prefer static checking).
Because the evaluation order of function designator, arguments, and subexpression within arguments is unspecified.
delete-for-new and delete[]-for-new[].
Legacy APIs (or non-owning APIs) may still require raw pointer, but they should not own the resource through raw pointer!
Copying resources is not always the behavior you want (you probably want to prohibit copying and support transferring ownership).
Just use RAII - and don’t let exception leave destructor.
The two copying functions: copy constructor and copy assignment operator. You must pay special attention if you override the default ones.
Self-assignment could happen when objects are aliased (e.g., *px = *py).
This is a convention that you should follow (unless you have great reason not to).
During construction and destruction, the base class’s constructor and destructor can only see base class’s virtual functions (the virtual function table is pointer to the base class) because at that point, derived classes do not exist (either is not initialized yet or has been destroyed).
If you need to handle release failure in RAII, add a close() function. Just don’t let exceptions leave destructors.
On the other hand, classes not designed to be base class or be used polymorphically should not declare destructor virtual.
Declare (but don’t define) what you don’t want and compiler won’t generate it (default constructor, copy constructor, copy assignment operator, and destructor).
The 4 bread-and-butter functions: default constructor, copy constructor, copy assignment operator, and destructor.
Avoid initialization order problem across translation units! (Or just don’t use globals?)
You may add const to almost anything.
Use as less preprocessor as possible (or even better, use as less non-zero globals as possible).
Meyers thinks there are 4 sub-languages within C++.
What are the ramifications of C++ global constants?
It is often considered a good project management practice that only a clearly specified subset of C++ is allowed in the codebase.