C++ Notes
lvalue and rvalue references
lvalue - anything that can be placed on the left side of an expression rvalue - literals (e.g. 5), temporary values (e.g. x+1), and anonymous objects (e.g. Fraction(5, 2)).
Below we can see an example of the lvalue and rvalue references usage:
Rvalue reference usage:
- Move semantic
- Perfect forwarding
Move semantic
When some complex objects are passed by value the copy constructor gets invoked to perform deep copy (doesn’t happen when it is passed by reference). If the object is an rvalue
then we will first create this rvalue object and then perform copy constructor
, then call destructor for the rvalue object. Instead of those unnecessary steps we can just move the rvalue object with move constructor
.
Move semantic is implemented for all STL containers.
Perfect forwarding
We would like to perform argument forwarding without costly and unnecessary copy constructor and keeping rvalue as rvalue / lvalue as lvalue.
Reference collapsing rules:
- T& & ==> T&
- T& && ==> T&
- T&& & ==> T&
- T&& && ==> T&&
So in the case above, when relay takes on an rvalue, after reference collapsing we get rvalue reference, otherwise an lvalue reference.
T&& is a universal reference if:
- T is a template type.
- Reference collapsing happens to T.
User defined literals
If we want to associate values with some metrics (m, cm, mm, …) we can define corresponding literals.
Compiler Generated Functions
C++ 03
1.Default constructor (generated only if no constuctor is defined by user)
2.Copy constructor (generated only if no 5, 6 defined)
3.Copy assignment operator (generated only if no 5, 6 defined)
4.Destructor
C++11
5.Move constructor (generated only if no 2, 3, 4, 6 defined)
6.Move assignment operator (generated only if no 2, 3, 4, 5 defined)
So if we declare an empty class in fact we get a class with 6 compiler generated functions:
Disallow Compiler Generated Functions
In C++11 we can dissalow Compiler Generated Functions with delete
modifier:
In C++03 just declare unwanted functions into private section without definition:
If we define destructor in private section, in order to destruct the object we need a helper function to do so. In this case we need to create the class object on heap. Creating an object on stack evantually will try to call the destructor - so this case won’t compile.
Const
const
before *
- pointer to a constant data, can’t dereference the pointer and change it’s value but can change the pointer itself.
const
after *
- constant pointer, can dereference the pointer and change the value it points to but can’t change the address it points to.
Hacky way to change a constant value:
Const function
A member function which doesn’t modify any member variable, can call only const functions, otherwise compiler error.
Const modifier can be used to overload functions:
When used as argument type modifier and arguments are passed by value can’t be used to overload functions - the case below won’t compile:
When used as argument type modifier and arguments are passed by reference can be used to overload functions:
Virtual destructor
Only part of the WhiteCat object is destroyed (base part). To fix this issue we need to declare the destructor of a base class as virtual
.
When at least one function in the base class is vurtual the virtual functions table, vftable
is created for the class. Each object of such class will hold a pointer to this table - __vfptr
(only a pointer not the copy of the table). So two objects will hold __vfptr
s to the same table. When we derive a child class from a base class with virtual functions the vftable
is copied from the base class and each object of the derived class will hold the __vfptr
to this new table. This table will hold pointers to the functions in the base class. So, if we don’t override virtual functions of the base class inside the child class, calling them from a child class will first invoke looking at the vftable
of the child class and call the corresponding function (which is base class function). If we override vurtual functions in child class then we replace vurtual functions (their addresses) in the vftable
of the child class with the addresses of overriding functions in child class. This mechanism allows dynamic polymorphism
.
So in the example above, when delete cat
is called, as the cat holds a pointer to the WhiteCat object (which has a pointer to its own vftable), we first need to decide which destructor to invoke - look at the WhiteCat’s vftable and find the destructor address (which is the WhiteCat’s destructor). After that destructor of the Cat is also invoked.
The cat1
and cat2
objects hold vfptr
s to the same vftable
- address is 0x00007ff7155cb858
.
Inside this table there are addresses of two functions - destructor 0x00007ff71555d48d
and func() 0x00007ff71555ec11
.
The cat3
object holds a vfptr
to a different vftable
- address is 0x00007ff7155cb8a8
.
Inside this table there are addresses of two functions - destructor 0x00007ff71555f030
and func() 0x00007ff71555ec11
. The destructor address is different from the cat1/2
s objects as it is overriden with the WhiteCat
s destructor. The address of the func() function is the same because it’s left untouched in the WhiteCat
class.
Another solution to correct object destruction is to use shared_ptr
s:
Static polymorphism
Dynamic polymorphism suffers from some runtime overhead. We can eliminate it by staticly resolving which method to invoke.
We have some inheritance structure (Base->Derrived) (1), base class defines some generic algorithm which is used by derrived class (2) and derrived class customizes this generic algorithm (3).
Public, Protected and Private inheritance
Suppose we have Base
class and Derrived_pub
, Derrived_prot
, Derrived_priv
which are childs of the Base
with public, protected and private inheritance.
Derrived_pub
inherets public and protected members as public and protected, has no access to the private members of Base
. Outside the class we still can access public interface of Base
through Derrived_pub
object.
Derrived_prot
inherets public and protected members as protected, has no access to the private members of Base
. Outside the class we can’t access public interface of Base
through Derrived_prot
object (but Childs of this class can).
Derrived_priv
inherets public and protected members as private, has no access to the private members of Base
. Outside the class we can’t access public interface of Base
through Derrived_priv
object (Childs of this class also don’t have access).