Variables
- A
pointer
is an object, areference
is not. - To understand the type of a variable, read the definition from right to left. The closest symbol defines what the variable is.
const
describes the type,constexpr
describes the expression (during compile time).- When defining a reference parameter in a function, use reference to
const
if 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
#pragma
guard header.
Expressions
&&
has higher precedence than||
.- Use c++ style casts instead of c-style ones.
Functions
- Check
initializer_list
- Define
inline
andconstexpr
functions in headers.
Exception and debug
- Use exception handling
throw
,try-catch
NDEBUG
,__func__
,__FILE__
,__LINE__
Class features
- Use
const
member functions - Member functions defined in the class are implicitly
inline
- c++ constructors must succeed, because they cannot report failures
- A
const
member function that returns*this
as a reference has a reference-to-const
return type - To construct a class, use constructor initialization instead of assignment
const
members are declared in the class body, and initialized in constructor initialization list- Delegating constructor
friend
static
class members are associated with the class, rather than with the objects. Thus,static
member functions cannot usethis
.static
data members should be defined and initialized outside the class body, unless it’sconstexpr
.static
members can be used as default arguments
Standard containers
<lib_type>.size()
returns a<lib_type>::size_type
value, 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_back
string
operations:append
,replace
,find
,to_string
,stoi
,stod
Generic algorithms
stable_sort
,unique
- Predicates
lambda
expressions
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 = v2
here 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 ifR
is the objectL
itself, 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.
=delete
flag indicates the member function is inaccessible.- If a class has
const
members, 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::move
to get thervalue
reference 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::move
to get thervalue
reference as the argument. In this case, thervalue
reference will be implicitly converted to aconst lvalue
reference.
OOP
- Inheritance: base and derived class,
protected
members - Keywords:
virtual
in base class andoverride
in derived class - Base classes should have a
virtual
destructor; 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
virtual
functions. - We use a reference or pointer to the base class to call
virtual
functions, 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
=0
flag. - 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
typename
orclass
; 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) { // ... }