Variables
- A
pointeris an object, areferenceis not. - To understand the type of a variable, read the definition from right to left. The closest symbol defines what the variable is.
constdescribes the type,constexprdescribes the expression (during compile time).- When defining a reference parameter in a function, use reference to
constif we are not going to modify the argument. - pointer to const annd const pointer are different, but reference to const is often abbreviated as const reference, because there is no such thing as a const or non-const reference - reference is not an object.
decltype(var)returns the type of its operand, butdecltype((var))(double parentheses) will always return the reference.- Use
#pragmaguard header.
Expressions
&&has higher precedence than||.- Use c++ style casts instead of c-style ones.
Functions
- Check
initializer_list - Define
inlineandconstexprfunctions in headers.
Exception and debug
- Use exception handling
throw,try-catch NDEBUG,__func__,__FILE__,__LINE__
Class features
- Use
constmember functions - Member functions defined in the class are implicitly
inline - c++ constructors must succeed, because they cannot report failures
- A
constmember function that returns*thisas a reference has a reference-to-constreturn type - To construct a class, use constructor initialization instead of assignment
constmembers are declared in the class body, and initialized in constructor initialization list- Delegating constructor
friendstaticclass members are associated with the class, rather than with the objects. Thus,staticmember functions cannot usethis.staticdata members should be defined and initialized outside the class body, unless it’sconstexpr.staticmembers can be used as default arguments
Standard containers
<lib_type>.size()returns a<lib_type>::size_typevalue, which is unsigned, so avoid using ints along withsize().lib_type<T> e2(e1)is equivalent tolib_type<T> e2 = e1.string.c_str()returns a pointer toconst char.swap(v1, v2),emplace_backstringoperations:append,replace,find,to_string,stoi,stod
Generic algorithms
stable_sort,unique- Predicates
lambdaexpressions
Dynamic memory
shared_ptr,unique_ptr- Use smart pointers rather than built-in pointers
Constructor and destructor
- Default constructor:
vector<T> v1(val)- direct initialization - Copy constructor:
vector<T> v2(v1)(equivalent tovector<T> v2 = v1) - copy initialization - Assignment:
vector<T> v3; v3 = v2;- copy-assignment operator, overloaded - Thev3 = v2here is equivalent tov3.operator=(v2). - When writing copy-assignment constructors, it is best to first save the right-hand operand (say
R) to a local temporary before destroying any members of the left-hand operand (L). Such that even ifRis the objectLitself, we will still have a copy after freeing the memory. - Destructor: a class usually needs a user-defined destructor if it manages resources outside the class object. The destructor is used to free the allocated resources. In this case, we cannot use synthesized copy constructor. Therefore, if a class needs a user-defined destructor, it needs to define a copy constructor and copy-assignment operator as well.
=deleteflag indicates the member function is inaccessible.- If a class has
constmembers, the compiler will not synthesize a default copy-assignment operator for it. - To reallocate (move) an object without copying it, we first need to use
std::moveto get thervaluereference to the object, then we define a move constructor, whose parameter is an rvalue referenceClass &&. - If a class has defined its copy constructor, copy-assignment operator or destructor, the compiler will not synthesize the move operations at all.
If we do not define a move constructor, the object will be copied, even if we use
std::moveto get thervaluereference as the argument. In this case, thervaluereference will be implicitly converted to aconst lvaluereference.
OOP
- Inheritance: base and derived class,
protectedmembers - Keywords:
virtualin base class andoverridein derived class - Base classes should have a
virtualdestructor; constructors, on the other hand, should not bevirtual. - There are two kinds of member functions in base classes, one that would be inherited directly by the derived without any changes, the other that would be overridden, which are
virtualfunctions. - We use a reference or pointer to the base class to call
virtualfunctions, such that we can use the base object or the derived object as the argument - this mechanism is called dynamic binding. - The reference or pointer to the base can be bound to a derived object - there will be an implicit derived-to-base conversion.
- Derived-class objects should not assign values to base members. Use interface instead.
- The derived-to-base conversion only applies for conversions to a reference or pointer type. When initializing or assigning a base object with a derived object, we are actually calling the constructor or assignment operator of the base class. When this is happening, we only copy the base part members of the derived object and the derived-class part is sliced down.
- Abstract base class has pure virtual functions, which have
=0flag. - The derived destructor is run first, then the base-class destructor - opposite order from the way they are constructed.
- Base and derived objects cannot be stored in ONE container, so use containers to hold the (smart) pointers to these objects.
- Base pointers cannot access derived class members, even if the pointer is pointed to a derived class object. However, if we know for sure that it is a derived object, we can access it through a cast:
static_cast<Derived*>(basepointer)->derived_member;
Template
- Templates should be declared and defined in the header file, along with declarations of any names used.
- A template will generate code only when it is instantiated.
- A function template has a parameter list, in which the parameters are separated by commas
,:template <typename T> T foo(T *p) { T tmp = *p; // ... return tmp; } - Template type parameters are used as type specifiers, followed by the keyword
typenameorclass; nontype parameters represent a value rather than a type. - In a function template, better define parameters as
references to const. - A class template looks like:
template <typename T> class XXX { private: /* data */ public: /* member functions */ // ... } - A list of explicit template arguments are required to be bound to the class template’s parameters during instantiation.
- Members of an instantiated class template will be instantiated only when they are used.
- A nontemplate friend of a class template will have friend access to all instantiations; a template friend will have a controlled friendship, which can include all instantiations or some specific instantiations.
- Explicit instantiation and specialization
// definition template <typename T> T* foo(T *p) { // ... } // explicit instantiation template int* foo<int>(int *p); // specialization template <> int* foo<int>(int *p) { // ... }