C++

Notes about C++ Constructs and Concepts


Last Updated: September 01, 2018 by Pepe Sandoval



Want to show support?

If you find the information in this page useful and want to show your support, you can make a donation

Use PayPal

or

Use/Enable Monetization

Not Monetized

This will help me create more stuff and fix the existent content...


C++

  • C++ is strongly typed, this means every single variable has a type, it doesn't change its type

Paradigms

C++ is a general purpose programming language with a bias towards systems programming which supports the following coding paradigms:

  • Object-oriented programming
  • Procedural programming
  • Functional programming
  • Generic programming

C++ is used to write code at all levels, including firmware, operating systems, and large-scale applications.

Character Data

  • The standard char type is used to represent the numeric values for character data as represented by the basic character set present on a particular computer. This is determined by the locale settings.
  • For internationalization purposes, the wchar_t type is used which expands on the numeric values available to represent character sets from various languages found around the world (E.g a character data that doesn't fit in the standard ASCII character set, such as Japanese kanji)

Enums

  • An enumeration is a set of constants stored as literal values. With an enum, you specify a limited set of literal constants that can be assigned to the type
  • Internally, the constants in the enum are used as numbers and not as the textual representation you assign to them

enum Month { January = 1, February, March, April, May, June, July, August, September, October, November, December };

Variable Initialization

  • C++ allows C style variables initialization besides curly brace init {} and parenthesis ()
  • You can use an empty pair of braces to init to default values usually zero
    int myVar = 0;
    int yourVar{1};
    bool flag{ true };
    int zeroVar{};
    double myDouble(53.53);
    

C++ components

  • C++ provides the standard template library (STL) that consists of many classes and functionality.

C++ Namespaces

  • A namespace is a "scope container" where you can place your classes, variables, or other identifiers to prevent conflicts with others. Anytime you want to reduce ambiguity or name collisions, you use the namespace designator ahead of the identifier. E.g. using namespace MyNameSpace (Avoid using the this using namesoace in headers as it will then be included whenever the header is included and will highly likely result in name clashes)

  • The :: is the scope resolution operator and allows you gain access to classes, variables, or other identifiers in the std namespace. E.g. std::cout means the cout object exists in the namespace std

  • You can define your own namespace by using the keyword namespace and nested namespaces if you choose to provide more separation of identifiers in your code. Namespaces can be combined to form new namespaces as well

namespace MyNameSpace {
    namespace Geometry {
        const double PI = 3.14159;

        double Area(double r) {
            return PI*(r*r);
        }
    }
}

//Combined namespace
namespace MyNameSpace_std {
    using namespace MyNameSpace;
    using namespace std;
}

using namespace MyNameSpace;

int main() {
    double radius = 12.5;
    double area = Geometry::Area(radius);
}

C/C++ Dereferencing table

Expression Description Example
*p++
*(p++)
Extracts value pointed by p then p is incremented *p++ = 1; is equivalent to *p = 1; p++;
(*p)++ The value that p points to is incremented (*p)++; is equivalent to *p = *p + 1;
*++p
*(++p)
First increment p then the new address that p points to now (after the increment) is extracted *++p = 1; is equivalent to p++; *p = 1;
++*p
++(*p)
Increments the value pointed by p then extract the value pointed by p (evaluates *p) ++*p = 1; is equivalent to (*p)++; *p = 1;

Class & Object

  • A class is the blueprint used to generate objects
  • An object is an instance of a class
  • In a program execution a class does not have life in memory (it's just code) and object does have a space in memory
  • Define the data (Attributes) and actions (Functions/Methods) of the object they represent
  • Objects contain only the data (attributes/members/properties), for the methods the compiler creates ONLY one copy of code of all the class methods and all the objects of the same class share the code of these functions
    • For this reason using sizeof on a class will return the sum of all the members/attributes (the size of the methods is omitted for this calculation)

Constructor and Destructors

Constructor

  • It's a method that is executed when an object is created usually to init the object to a known state, this means initialize the object members/attributes
  • Constructors are methods that do NOT have a return type
  • Every class has a constructor even if it's not explicitly defined by a programmer the compiler will create an implicit constructor
    • The compiler will create a default constructor automatically, depending of the compiler it could or not initialize member variables. The default constructor, if provided, is one that does not have any parameters

Attributes in an object can also be initialized using the brackets initializer {}, empty braces will call the default constructor, if defined. E.g. class Rectangle { public: int w; int h; }; Rectangle myRec{10, 7}; Rectangle otherRec{};

Destructor

  • Method that is executed before an object is destroyed (released from memory)
  • It's a method that does NOT receive parameters NOR returns any values
  • Destructor overloading is not permitted
  • Just like a constructor every class has a destructor even if it's not explicitly defined

Constructor of a static object is called only once when the program execution reaches for the first time the place where the object is defined

// file: person.hpp
#include <iostream>

class Person {
    public:
        Person(std::string name, unsigned int age); // constructor
        void print_person_info(void);
        ~Person();  // destructor
    private:
        std::string name;
        unsigned int age;
};


// file: person.cpp
#include "person.hpp"

Person::Person(std::string name, unsigned int age) {
    this->name = name;
    this->age = age;
}

void Person::print_person_info(void) {
    std::cout << "Person name: " << this->name << " age: " << this->age << std::endl;
}

Person::~Person() {
    std::cout << this->name << " Person Object Destructor" << std::endl;
}

References

  • They are like pointers because they actually modify the memory of the variable/object they are referring

  • It can be thought as an alias, that is, another name for an already existing variable.

    • When a variable is declared as reference, it becomes an alternative name for an existing variable
  • A variable can be declared as reference by putting & in the declaration

  • Differences with pointers:

    • A pointer can be declared as void but a reference can never be void
    • Once a reference is created, it cannot be changed to reference another object, a references is like a pointer that can only points to a certain variable/object
    • References cannot be NULL. A reference must be initialized when declared
// file: main.cpp
// g++ -std=c++11 -Wall ./main.cpp -o test
#include <iostream>
#include <memory>

// Using Pointers
void swap_ptr(int* a, int* b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

// Using C++ References
// when called references 'a' and 'b' are created and refer to the variables passed as parameter
// so 'a' and 'b can modify those variables which are defined outside the scope of this function
void swap_ref(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}

int main(int argc, char *argv[]) {
  int x = 7, y = 17;

  swap_ref(x, y);     // with references
  swap_ptr(&x, &y);   // with pointers
}

const Objects

  • Objects that are initialized but can't be modified (the compiler will enforce this)
  • const objects CAN'T make a call to their methods unless these are defined as const as well (E.g. int get_area () const { return w*h; }
  • const functions defined inside a const object cannot modify the object either
  • A good implementation practice is to define get functions as const
  • Constructors and destructors cannot be declared as const
  • const functions can be overloaded with non-const functions and the one called will depend of the object that is calling it, if the object is const the const function version will be called if not the non-const version will be called instead

C/C++ const pointers table

Declaration Case Description
int* ptr No const pointer and No const data The pointer can be modified to point to other data and the data that it points to can be modified
const int* ptr No const pointer and const data The pointer can be modified to point to other data but the data that it points to CAN'T be modified using the pointer
int* const ptr = &x const pointer and No const data The pointer can only point to the value it was initialized, we CAN'T change it to point to different data but we can modify the data using the pointer
const int* const ptr = &x const pointer and const data The pointer can only point to the value it was initialized and the data that it points to CAN'T be modified using the pointer either

Member initialization (: operator in constructors)

  • Initialization done in the constructor
  • These type of initialization is required for const members
  • We can have multiple Member initializations separated by commas
  • It is the way we must use to call a base class constructor (parent constructor)
// file: student.cpp
...
// Base class constructor (Person in this case) must be called from the initialization list
// RACE is a const member
Student::Student(std::string name, unsigned int age, std::string school) : Person(name, age), RACE("human") {
    this->school = school;
}
...

Object Composition

  • Object composition means having objects as part of a class, in other words as attributes/members of a class
  • Objects are constructed inside-out (from the inside to the outside) so member objects are always created first than the object that encapsulates them, they are created in the order in which they are declared in the class
  • It's a good practice to init member objects with the member initializer :

Friend function

  • A class can define an external function as a friend function. This allows the friend function to access all the members of the class (public and private) but these functions

  • are non-members, which means they don't receive a this pointer. Consequently, they must require an explicit parameter to access an object.

  • Friend functions are declared in the class definition (usually .hpp) with friend and then implemented in the .cpp but without the ClassName:: syntax (because they are not members of the class)

  • The implementation of friend functions can be in any other place it's desired, if they are implemented in the class that does NOT make them methods of the class

Friend Class

  • A class can define another class as its friend. A friend class can access all the members of the first class (the one that made the friend class definition).

this pointer

  • It's a pointer to the object
  • Every object has access to it's own memory address, this values it's stored in the pointer this, so we can use it to access the object attributes and methods
  • The compiler passes to all the methods of a class (implicitly) the pointer this as argument (except to the static methods)

Dynamic allocation in C++ new & delete

  • The new operator is used to dynamically allocate memory and when used does the following:

    1. Reserves in memory the space needed to store an object or variable (on the right side of the operator)
    2. Creates the object or variable in memory (if it's an object constructor is called)
    3. Returns a pointer of the type specified by the object or variable to the right side of the operator
  • The delete operator is used to free/release allocated memory and when used does the following:

    1. Calls the destructor if it's an object
    2. Destroys/releases the object from memory freeing the space that was reserved by that object
  • Example:

Time* timePtr = new Time(1, 1, 1);
delete timePtr;

int* array = new int[10];
delete [] array; // to delete dynamically allocated array through a pointer we must use [] to delete all elements

Allocation

Allocation failures

  • Allocation failures can be divided into two areas Memory exhaustion and Heap corruption
    • Memory exhaustion: These errors means the system has run out of memory
    • Heap corruption: Errors are caused due to pointer that point to invalid objects/memory (objects/variables that have already been released) or because something else has corrupted/overwritten the section a certain object is using in heap like for example an out of bounds write to a section used by another object

static class attributes & methods

  • Attributes that are shared between all objects of a class, all the objects created from a class share these type of members
  • It's an attribute of the class and NOT of the object
  • They can only be initialized once
  • They exists even if NOT a single instance (object) of the class
  • They can be used even if an instance doesn't exists, we can access them using the :: operator
  • A method declared static can ONLY access static attributes, this means it can NOT access non-static attributes
  • A static method does NOT have a this pointer because this pointer must refer to a specific object of the class
  • static functions cannot be const (TODO: need to check reason for this)
  • Usually declared in the .hpp and initialized in the .cpp using the :: operator

Operator overloading

  • It allows objects to be used with operators like +, -, /, ++, *, etc.
  • Allows C++ supported operators to be used with objects defined by the programmer
  • It's a nice to have feature since it can be replaced completely using or implementing the right methods but it could be more tedious, although in general it's thought that avoiding operator overloading produces easier to read code for unexperienced C++ programmers
  • The functions to implement Operator overloading can be Member functions or Global Functions and it's implementation and how it will work changes depending on this
    • Member Functions:
      • Most common implementation of operator overloading.
      • The operand on the left (or only operand in some cases) will/must be an object of the class where the operator overloading function is implemented, In other words it is assumed that one of the operands will be an object of the class where the operator overloading function is implemented
        MyClass& MyClass::operator=(const MyClass&) { . . . }
        MyClass obj1, obj2;
        obj1 = obj2; // equivalent to obj1.operator=(obj2);
        
    • Global Functions
      • Don't belong to a particular class but, if needed, to access the attributes of a class friend functions can be used
      • Operand on the right must be an object of a class where the operator overloading function is implemented

Operator overloading: Syntax

  • A non-static function is needed since they need specific objects to operate on
  • Usually for the operator overloading function parameters we use references, objects are also accepted but it's more common to use references to objects
<return type> operator<operator symbols>(<parameters...>)
void operator+(int x, MyNumClass& num)
  • Example:
using namespace std;
class Point { public: int x, y; Point(int px, int py){x = px; y = py;}  };
istream& operator>>(istream &is, Point& p) { is >> p.x >> p.y; return is; } // operator overloading implementend using a Global function

ostream& operator<<(ostream & os, const Point& p) { os << "[" << p.x << "," << p.y << "]" << endl; return os; }
main() {
    Point p(7,17);
    cin >> p;  // this is equivalent of calling -> operator>>(cin, p); as it were a regular function
    cout << p; // this is equivalent of calling -> operator<<(cout, p);
}

Operator overloading: Unary operators

  • Operator overloading Unary operators and Member Functions Implementation: Member functions without arguments, it's assumed that the only operand will be an object of the class where this function is being implemented
bool MyClass::operator!(void) { . . . }
MyClass obj;
if(!obj)    // equivalent to if(obj.operator!())
{
 . . .
}
  • Operator overloading Unary operators and Global Functions Implementation: Global functions with one argument that must be an object or a reference to an object
bool operator!(const MyClass& refArg) { . . . } // or bool MyClass::operator!(const MyClass objArg) { . . . }
MyClass obj;
if(!obj)    // equivalent to if(operator!(obj))
{
 . . .
}

Operator overloading: Binary operators

  • Operator overloading Binary operators and Member Functions Implementation: Member functions with one argument, it's assumed that the other operand will be an object of the class where this function is being implemented
bool MyClass::operator>(const MyClass& refObj) { . . . }
MyClass obj1, obj2;
if(obj1 > obj2)    // equivalent to if(obj1.operator>(obj2))
{
 . . .
}
  • Operator overloading Binary operators and Global Functions Implementation: Global functions with two arguments that must be objects or a references to objects
bool operator>(const MyClass& refObj1, const MyClass& refObj2) { . . . }
MyClass obj1, obj2;
if(obj1 > obj2)    // equivalent to if(operator>(obj1, obj2))
{
 . . .
}

Copy Constructors and Overloading Assignment Operator (Equals operator = with objects)

  • A copy constructor is used to make a copy of an existing object instance. If you do not declare a copy constructor the compiler will generate one for you automatically.

  • The compiler generated one is only capable of a shallow member copy, If you have pointers to memory locations in your class you will need to create a copy constructor to copy the data pointed by these pointers correctly/explicitly

  • There are 4 possible function signatures for a copy constructor.

    1. MyClass(const MyClass& other);
    2. MyClass(MyClass& other);
    3. MyClass(volatile const MyClass& other);
    4. MyClass(volatile MyClass& other);

When the operator = is used in the initialization of an object, implicitly the compiler will use the copy constructor in case the equals operator (=) is not overloaded, so if we want to ensure a behavior defined by us we must overload the operator accordingly or explicitly implement the copy constructor with our code. E.g. .hpp: MyClass& operator = (const MyClass &rhs); .cpp: MyClass& MyClass::operator = (const MyClass &rhs)

Overload/Override new operator

  • We can override the new operator in a class to customize memory allocation for example to allocate memory from a custom memory pool instead of the free store, this means a special memory pool for our application
#include <new>
#include <iostream>

using namespace std;

//Allocator class
class MyAllocator {
  public:
    MyAllocator() {}
    ~MyAllocator() {}
    static void* operator new (size_t size);
    static void operator delete (void *p);
};

void* MyAllocator::operator new (size_t size) {
    void* p = malloc(size);
    if (p == NULL) {
        std::bad_alloc exception;
        throw exception;    // Throw bad_alloc.
    }
    cout << "MyAllocator::operator new called with size_t " << size << endl;
    return p;
}

void MyAllocator::operator delete (void* p) {
    cout << "MyAllocator::operator delete called. " << endl;
    delete[] p;
}

int main() {
    MyAllocator *p = new MyAllocator; //Create MyAllocator object.
    delete p; //Delete MyAllocator object.
    return 0;
}

Inheritance

  • It's a code reutilization technique that allows to create new SW components from existing ones. It allows them to obtain all or certain attributes and methods of an already defined component without the need to duplicate code

  • base class == super class == parent class

  • sub class == derivative class == child class

  • Constructors and destructors are not inherited in the same way as the other methods, they are available to the derivative class but in general their own versions must be implemented

    • If you don't explicitly call the base constructor in the derivative class, it will be called anyway by the default constructor of the derivative class.

Types of Inheritance in C++

  • public:

    • All the public and protected objects and methods of the base class are inherited as public and therefore can be accessed in the derivative class
    • The derivative class cannot access the private attributes or methods of the base class
  • private

    • Makes the base class public and protected functions private in the derived class, this would make these functions inaccessible outside of the derived class or any further derived classes
  • protected

    • Makes the base class public and protected functions protected in the derived class. This means that any further derived classes would have access to these functions but not external classes.
class ChildClass :  ParentClass {
  . . .
};

class MyDerivClass : public MyBaseClass {
  . . .
};

Inheritance constructors and destructors

  • Objects that inherit from other objects are created from the top to the bottom of the hierarchy tree
  • Objects that inherit from other objects are destroyed from bottom up to the top of the hierarchy tree
  • When an object is instantiated first the base constructor is called (if it's decided to be called) then the derivative constructor

Polymorphism: virtual & non-virtual functions

  • Polymorphism refers to the capacity of the object's methods to behave differently depending on the context of execution
  • Using Polymorphism means a function can execute different actions depending on the object that is calling it
  • Many of the usages of Polymorphism come from the use of pointers and references and the concept that derivative classes can also be treated as if they were base classes but base classes cannot be treated as if they were derivative classes

non-virtual functions

  • When we call non-virtual functions the reference and/or pointer is the one that decides the members/methods to be accessed
  • Regular member functions that DON'T have the virtual modifier are considered in general non-virtual functions and when we call them using a pointer or a reference is when Polymorphism can happen
  • When we call non-virtual functions using an object that DOES NOT inherit from another class (E.g. MyClass Obj; Obj.print()) Polymorphism CANNOT occur
  • Polymorphism happens when we used pointers or references and we are using objects in an inheritance hierarchy
class BaseClass {
    public:
        BaseClass() { ... }
        void print(void) { ... }
};


class DerivClass public: BaseClass{
    public:
        DerivClass() : BaseClass() { ... }
        void print(void) { ... }
};

DerivClass dObj;
BaseClass* bPtr = &dObj;
bPtr->print(); // if 'print' is NOT virtual, this calls function implemented in the BaseClass

virtual functions

  • When we have a function with the virtual modifier the object that is being referenced in the call is the one that determines which method will be called, it doesn't matter what type of pointer or reference we are using
  • virtual modifiers are only added in the functions prototypes (in the .hpp file) not needed in the function implementation (.cpp file)
  • The most common use case of virtual functions is to use pointer/reference from base class to call methods from a derivative class
    • To override a virtual function in a derived class, re-declare the function in the derived class header file. The function signature must match that in the base class and If you want to leverage the existing functionality of the base-class version of the function, you can call the function using the syntax BaseClassName::FunctionName()
class BaseClass {
    public:
        BaseClass() { ... }
        virtual void print(void) { ... }
};


class DerivClass public: BaseClass{
    public:
        DerivClass() : BaseClass() { ... }
        void print(void) { ... }
};


DerivClass dObj;
BaseClass* bPtr = &dObj;
bPtr->print(); // if 'print' IS virtual, this calls function implemented in the DerivClass

pure virtual functions

  • A pure virtual function is a function that is NOT implemented in base class and forces the derivative class to implement it
  • Regular virtual functions can or cannot be implemented and give the option to the derivative class to overwrite the functionality or not but pure virtual functions MUST be implemented in the derivative class or it will produce a compilation error
  • A class with one or more pure virtual functions is called an abstract class, usually used as the top of a classes hierarchy to force classes that inherit from it to implemented a certain interface (set of functions)
    • Abstract classes are the way we define a common interface for a set of classes or classes hierarchy
class MyAbsClass {
    public:
        MyAbsClass() { ... }
        virtual void print(void) = 0; // pure virtual function
};

virtual destructors

  • When we are destroying a derivative object using a a pointer from it's base class, if the constructor on the base class is NOT declared as virtual only the destructor on the base class will be executed
  • If we are using a base pointer to free a derivative object it is recommend that the destructor in the base class be declared as virtual
  • In general if a class has virtual functions it's destructor should be declared as virtual (although there could be exceptions)
  • By defining a virtual destructor in the base class, you ensure that the correct destructor is always called when you delete an object

Multiple Inheritance

  • C++ supports multiple inheritance, these means we can inherit form multiple classes and can be implemented with regular inheritance syntax or with templates (not very common to be implemented with templates but achieves equivalent results)

  • With multiple inheritance it exists the possibility of the diamond problem which can be solved by adding thevirtual keyword when declaring inheritance of a class to make it explicit that what determines which method will be called is the object being referenced

#include <iostream>

using namespace std;

class A { public: void printName(void) { cout << "Class A" << endl; } };

// Removing the virtual identifier when declaring inheritance in classes A and B
// should cause errors due to calling ambiguity when trying to invoke printName()
// It is equivalent to declaring all the methods with the virtual modifier
class B: public virtual A { public: void printName(void) { cout << "Class B" << endl; } };
class C: public virtual A { public: void printName(void) { cout << "Class C" << endl; } };

// BC presents the diamond problem because it inherits from B and C
// which at the same time each inherits from A creating an inheritance "diamond"
class BC: public B, public C { public: void printName(void) { cout << "Class BC" << endl; } };

int main(int argc, char *argv[]) {
    BC bcObj;
    bcObj.printName(); // calls printName method from BC class due to virtual inheritance
}

Casting

  • For explicit conversion we can use a type cast statement like in C or use the static_cast<type>(variable) operator. This is referred as Static Cast and is used to convert from one type to another and the user has to ensure the type cast is safe. This is usually used to convert a pointer from a base class to a derived class and from a derived class to a base class
MyDerived* md = new MyDerived();
MyBase* mb2 = static_cast(md); // Safe cast since MyDerived Contains MyBase public members from inheritance.
  • Const Cast is used to add or remove the const modifier to a variable this allows the developer to make a non-modifiable variable modifiable or a modifiable variable non-modifiable, also to change a variables volatile modifier.
const int MYCONST = 5;
volatile int myvolatile = 9;

int* nonconst = const_cast<int*>(&MYCONST);
int* nonvolatile = const_cast<int*>(&myvolatile);
  • Reinterpret cast will convert pointers between types and integers.
UnRelated* urc = new UnRelated();

// Very likely unsafe cast because type UnRelated is not related to MyBase class
MyBase* rcast = reinterpret_cast<MyBase*>(urc);

cast/casting Operator overloading & Conversion constructors

  • A conversion constructor is used when converting between types and it can be any constructor in the class that has only one argument,
  • These can be implicitly be called by the compiler when we attempt to convert between types or when we assign an object of a certain with another from a different type
  • We can avoid implicit calls to the conversion constructors if we defined operator, alternatively we can implement the conversion constructors with our code
  • When defining Operator overloading for a casting operator we DON'T specify the type of the return value since it's assumed that must be of the type it was converted
  • One of the most common usages of overloading the casting operators is to convert objects to fundamental data types (or to other object types)
  • The explicit keyword is used to specify a conversion constructor or function cannot be used for implicit conversions so it is mainly used to declare a conversion constructor can only be used for explicit conversions.
MyClass::operator char*() const { . . . }
myClass::operator double() { return 3.1416; }
MyClass obj;
static_cast(obj);    // equivalent to obj.operator char*()
static_cast(obj);    // equivalent to obj.operator double() or (double) obj;

double xd = 2.241234;
int xi = static_cast(xd); // equivalent to int xi = (int) xd; in C

Since any constructor with only one argument can be interpreted by the compiler as a conversion constructor and called in certain situations without us being aware we can use the reserved word explicit to avoid this situation and stop the the compiler from implicitly calling the constructor

Dynamic Cast

  • C++ supports RTTI (Run Time Type Information) which is the ability to determine the type of any variable during execution, it uses a Dynamic Cast to run time type check when performing the cast
  • Dynamic cast can only be used on pointer and references to objects
  • It is an operator that helps making a cast to base pointers that are pointing to different types of Derivative objects and determine what specific derivative object are they pointing in a given time
  • One common use case is to determine what type of object is a pointer actually pointing to
  • Syntax:
    • dynamic_cast<DerivClassA*>(basePtr) if basePtr points to a DerivClassA object it returns address of that object (pointer of type DerivClassA) if not returns 0/nullptr instead;
Person *basePtr;
Employee* emPtr;

basePtr = new Student ("Student", 23, "School"); // if this line is active basePtr points to an 'Student' object
//// or
//basePtr = new Employee ("Employee", 25, "Company"); // if this line is active basePtr points to an 'Employee' object

emPtr = dynamic_cast(person); // returns '0'/nullprt/NULL if not pointing to 'Employee' object
if(emPtr == nullptr) {
    std::cout << "basePtr is NOT pointing to an Employee object" << std::endl;
} else {
    std::cout << "basePtr IS pointing to an Employee object" << std::endl;
    std::cout << "You can now access that object through the emPtr pointer as well" << std::endl;;
}

auto

  • The keyword auto is used to let the compile determine the appropriate type for a variable based on the context of it's initialization (type inference)
  • We CAN NOT use brackets initialization {} when we use auto

Exceptions

  • The C++ standard library provides an exception base class that can be derived and allows us to override the what() method to return a custom error message string and also override it's virtual destructor in case we want to release memory

  • If you define an ellipsis (...) as the parameter of an exception handler then it will catch any exception type that is thrown in its associated try-block. This is most often used as a default exception handler when no other exception handler type matches.

  • An exception is uncaught if there is no associated exception handler to catch it. When this happens a call to terminate() is done automatically and any subsequent calls to termination functions that would normally be done during normal program execution does not happen, this includes calls to allocated objects destructor's functions.

  • In general it is not considered good practice to throw exception in an objects destructor because it could cause unhandled exceptions. E.g. if an object is declared inside a try statement and another exception occurs there, when destroying the object for going out of scope the destructor will be called and if it throws another exception this will be an unhandled exception

#include <iostream>
#include <exception>

using namespace std;

class derivedexception: public exception {
    virtual const char* what() const throw() { return "My derived exception"; }
} myderivedexception;

int main () {
    try { throw myderivedexception; }
    catch (derivedexception& e) { cout << e.what() << '\n'; }
    catch (...) { cout << "All other exception types." << endl; }
}

  • Throwing exceptions
void myfunction() noexcept;   //Function does not throw exception, equivalent to noexcept(true)
void myfunction() noexcept(true/false);   //Throws an exception depending on boolean true/false. true -> does NOT throw, false -> throws
void myfunction(void pfa() noexcept);  // g takes a pointer to function that doesn't throw
void myfunction() throw ();   // Old way (before C++11) to specify that throws exception
void myfunction() throw (class_name);   // Old way (before C++11) to specify that throws exception of specific class usually a class that inherits from `exception`

Move Operations

  • T& lvalue; lvalue reference

    • An lvalue reference can NOT be moved once its assigned.
    • Any expression that can appear on the left side of an assignment, it means its legal for it to appear on the left side
    • It can also appear on the right side but it would still be lvalue
  • T&& rvalue; rvalue reference

    • An rvalue reference can be moved.
    • rvalue is any expression that may appear on the right side of an assignment
    • An rvalue CAN be moved
    • prvalue:
      • it's an expiring value, is a temporary value ready to expire like the result of an expression
      • anything returned from a function that is not a reference
  • The C++ library provides a template function called move. Which works more like a type cast. It's used to tell the compiler to use an object as if it were an rvalue, which means that it makes it moveable.

Templates

  • Template are a C++ language feature for code reuse and "Generic Programming"
  • Templates allows us to use the function for multiple types without changing the function for specific types, they create a generic function and we can apply it to many different types
  • We can use template<typename T> or template<class T> to define a template function or class where T specifies the type that will be varied, using either option is equivalent but the template<class T> is usually used for classes just for clarity, to the compiler they mean the same
  • To use a Template we can explicitly specify the type or we can let the compiler perform type deduction if we don't provide the type but this lead to the problem that template functions cannot be overloaded in a traditional way
  • If we want we can pass multiple types to a template or define a default type
  • We can also pass a constant expression which is evaluated at compile time or the address of a function or object or the address of a static class member instead of types to a template, and these can also have default values
    • However doubles and floats can only be passed as pointers and not by value as defined by the C++ standard

Templates are determined at compile time, so actually the compiler is generating all the necessary code and including it for us

#include  <iostream>
#include  <vector>
#include  <list>
#include  <map>
#include  <iterator>

using namespace std;
const double pi = 3.1415926535897;

class Circle {
    public:
        Circle(int rad) : radius(rad) { area = pi*radius*radius; }
        const bool operator < (const Circle &r) const { return (area < r.area); }
        friend std::ostream& operator<< (std::ostream& stream, const Circle& cir) { stream << cir.area << "\n"; return stream; }

    private:
        int radius; double area;
};

template<typename T>
void printTypeName(T x) {
    cout << typeid(x).name() << endl;
}

template<typename TContainer>

void bubbleSort(TContainer begin, TContainer end)
{
    TContainer i, j;
        for ( i = begin; i != end; i++) {
            //Loop around all elements in container.
            for ( j = begin; j != i; j++) {
                //Loop around container comparing current element to all others and bubble up.
                if (*i < *j) {
                    //Swap elements.
                    std::swap(*i, *j);
                }
            }
        }
}

int main() {
    int myint = 5;
    printTypeName<int>(myint);
    printTypeName<>(myint);
    printTypeName(myint);

    Circle c(7);
    printTypeName(c);

    vector<Circle> circleVec;
    circleVec.push_back(Circle(5));
    circleVec.push_back(Circle(4));
    circleVec.push_back(Circle(3));
    circleVec.push_back(Circle(2));
    circleVec.push_back(Circle(1));

    bubbleSort(circleVec.begin(), circleVec.end());
    for (vector<Circle>::iterator it = circleVec.begin(); it != circleVec.end(); it++) {
            cout << *it;
    }

    return 0;
}

Template Classes

  • It is possible to create generic classes like we did with template functions so certain members of the class can be of generic type and we can inherit from a specific template class (specifying the parameter Type) as well
#include <iostream>

using namespace std;

template<class T = int>
class Point{
    public:
        Point() {}
        Point(T x, T y): px(x), py(y) {}

    private:
        T px;
        T py;
};

template<class T1, class T2, int size = 1>
class MultiPoint{
    public:
        Point() {}
        Point(T1 x, T2 y): px(x), py(y), s(size) {}

    private:
        T1 px;
        T2 py;
        int s;
};

int main() {
    MultiPoint myPoint(1, 3.1415161);
}

Template Specialization

  • Normally when we write a template class or function we want to use it with many different types, however sometimes we want to code a function or class to make use of a particular type more efficiently this is when we use a template specialization.

  • To define a specialized template we leave the parameters empty like template<> this is used if we define a template function but then we want to have an overloaded "specialized" function for an specific type

// Template function for most cases
template<typename T>
void printFunction(T arg) {
    cout << "printFunction arg is type " << typeid(arg).name() << " with value " << arg << endl;
}

// Specialized template for integers (for type 'int')
template<>
void printFunction(int intarg) {
    cout << "printFunction specialization with int arg only called with type " << typeid(intarg).name() << " with value " << intarg << endl;
}

// Template class for most cases
template<class T>
class MyClass {
    public:
        MyClass(T initVal) { myMember = initVal; cout <<"My Class template " << myMember << endl;}

    private:
        T myMember;
};

// Specialized Template class for integers (for type 'int')
template<>
class MyClass <int> {
    public:
        MyClass(int initVal) { myMember = initVal; cout <<"My Class specialized template " << myMember << endl;}

    private:
        int myMember;
};

Standard Container Classes

  • A container class is used to hold and organize objects of other classes. The containers are implemented as template classes which allows to be used with many different types. E.g. a vector is a container and can be:

    • Composition container which store copies of the objects and will call the constructors and destructors of those objects.
    • Aggregations container which means they store pointers or references to other objects and thus are not responsible for their creation and destruction.
  • Sequence containers are named so because their elements are accessed sequentially, they are vector, list, deque and array, forward_list (added in C++11)

  • Associative containers which are designed to provide efficient access to elements by key for example: set, map, multiset and multimap. by default they are ordered but there is an unordered version for each one (unordered_set, unordered_map, unordered_multimap, unordered_multiset)

    • map and multimap use key-value pairs and in set and multiset the value is the key. Keys must always be unique in all cases

Iterators

  • An iterator is an object that allows the traversal of a container

  • Types of iterators in C++:

    • Input:
    • Output:
    • Forward: Inherit the input iterator behavior and if they are non-const then the output iterator behavior as well, they are limited to a forward direction of traversal
    • Bidirectional: Inherit from forward iterators and are the same except they can also move in a reverse direction
    • Random access: Inherit all the bidirectional iterator behavior and can access elements non-sequentially (this means we can apply an offset directly to the iterator to access an element)
  • Iterators are used to implement generic algorithms, usually defined as a template function and if they change the elements in the container they will usually define an input and output iterator and copy the input iterator elements to the output iterator to avoid changing the original elements.

#include <iostream>
#include <vector>
#include <list>

template<class TypeRandIter>
void randomise(TypeRandIter iterBegin, TypeRandIter iterEnd) {
    while (iterBegin < iterEnd) {
        iter_swap(iterBegin, iterBegin + rand() % (iterEnd - iterBegin)); //randomInteger(iterEnd - iterBegin));
        ++iterBegin;
    }
}

int main(int argc,char* argv[]) {
    std::list<int> List = { 34, 77, 16, 2, 35, 76, 18, 2 };
    //randomise example.
    cout << "Calling randomise on sorted vector " << "\n";
    List.sort();
    for (std::vector<int>::iterator it = temp.begin(); it != temp.end(); it++) {
        cout << *it << " ";
    }
    cout << "\n\n";
    cout << "Results after calling randomise on sorted vector are " << "\n";
    std::vector<int> temp(List.begin(), List.end());
    randomise(temp.begin(),temp.end());
    for (std::vector<int>::iterator it = temp.begin(); it != temp.end(); it++) {
        cout << *it << " ";
    }
}

Common C++ Programming Patterns

  • An Interface is used to define the behavior of classes without saying how to implement it. In C++ usually implemented using abstract classes. They contain generalizations about the characteristics of related entities

  • The Command Pattern is considered a behavioral design pattern, It is like an object based version of a call back in which the requester sends a request as an object. Used to connect two object so a call in one object triggers code in another

  • A Function Object (also known as functors) is an object that behaves like a function meaning it is callable but since it's essentially and object it can also store data. To be callable it defines the call operator (operator())

  • The Handle-Body pattern allows developers to put implementation (body) and interface (handle) into two classes, then a client uses the handle class to interact with the body class

    • The Adapter pattern uses the handle-body idiom by wrapping classes into another interface which is the handle to the client.
  • Nested classes are classes declared inside classes, a nested class is a member of the enclosing class and has the same access rights as other members of the class. This means we can access the members of the enclosing class from the nested class. The enclosing class has no special access to the members of the nested classes

  • In the Association-Delegation pattern objects of one class are connected to another class in this association the controlling class does not manage the life cycle of the controlled class, it just uses the controlled class or in other words delegates a request to the controlled object however this controlled object can exist without the controlling object.

  • The Proxy pattern is used to communicate with another object that for some reason cannot be communicated with directly, the proxy is placed in between the client and the final destination for the communication.

  • A Member Function Pointer holds the offset of the function address in the objects list of functions, while a normal function pointer holds the address of the function to call. If it is a virtual function then it is an offset into the virtual table of function pointers.

    • If we want to use a function from an object through a function pointer we cant use a normal function pointer we need a member function pointer
    • You can only use them on behalf of an object to dereference the function this means you need the function pointer and an object
    • A member function pointer is defined like: return_type (className::*memberFunctionPtrName)(parameter_types) = &className::member_function_name; while a normal function pointer: return_type (*FunctionPtrName)(parameter_types) = &function_name;
class Base {
public:
    virtual void sayHello(string s) { cout << "Hello from Base class" << s << endl; }
};

class Derived : public Base {
public:
    virtual void sayHello(string s) { cout << "Hello from Derived class" << s << endl; }
};
...
void (Base::*baseFuncPtr)(string) = &Base::sayHello;
void (Derived::*derivedFuncPtr)(string) = &Derived::sayHello;

Base baseObj;
Derived derObj;

string str = "My String!";
(baseObj.*baseFuncPtr)(str);   // call Base::sayHello
(derObj.*derivedFuncPtr)(str); // call Derived::sayHello
(derObj.*baseFuncPtr)(str);    // call Derived::sayHello using base pointer and derived object.
  • The Singleton pattern means only one instance of a class is intended to be created, to implement this the class provides a public static access function that returns the single instance and creates it if not already created, this single instance is accessed through a private static attribute of the class (a pointer or reference), also the copy constructor, default constructor and assignment operator should be private to prevent them being invoked to create more objects
#include <iostream>
#include <string>

using namespace std;

// .hpp
class Singleton {
public:
    static Singleton* getSingleton() {
        if (!instance) {   // Only allow one instance of class to be generated.
            instance = new Singleton;
        }
        return instance;
    }

    void sayHello(string str) {
        cout << "Hello from singleton \n" << str;
    }

private:
    Singleton() {} //Private so cannot be invoked.
    Singleton& operator=(Singleton const& rhs) {}  //Private so cannot be invoked.
    Singleton(Singleton const& ) {};
    static Singleton* instance;
};

//.cpp Init Static member instance;
Singleton* Singleton::instance = NULL;

// main.cpp
int main(int argc, char* argv[]) {

    //Get instance create single object.
    Singleton::getSingleton()->sayHello("Hello world!!! \n");

    return 0;
}

Smart pointers / Shared pointers std::shared_ptr

  • std::shared_ptr is a smart pointer that retains shared ownership of an object through a pointer.

  • They wrap a raw pointer in a class so that when the class object is destroyed the destructor of the smart pointer object could also release the memory pointed by the raw pointer in case no on else is holding a reference to this memory, this helps avoiding memory leaks and allows smart pointers to be used for garbage collection.

  • Although they are objects, the std::shared_ptr class defines the operator* and operator-> to overload the dereference (*) and indirection (->) operators, so we can use smart pointer objects as regular pointers

class Test {
public:
    Test(int a = 0 ) : m_a(a) {}
    ~Test() { cout << "Calling destructor for Object with address: " << this << " with value " << m_a << endl; }
public:
    int m_a;
};

shared_ptr<Test> sptr(new Test(5));
cout<< "Count of sptr before other reference assigment " << sptr.use_count() << " " << sptr->m_a << endl;

{
    shared_ptr<Test> optr = sptr;
    cout<< "Count of sptr after other reference assigment " << sptr.use_count() << " " << optr->m_a << endl; // optr->m_a to prove optr points to the same object than sptr
}

cout<< "Count of sptr after other reference goes out of scope " << sptr.use_count() << " " << sptr->m_a << endl;

Streams

  • Streams are essentially a sequence of bytes that are sent from device to device

  • C++ defines a standard iostream library because input/output operations in C++ occur in streams

  • cin, cout and cerr are predefined objects in the std namespace

    • cin is an instance of the istream class, and allows you to perform input from the stdin device.
    • cout is an instance of the ostream class, and allows you to perform output to the stdout device.
    • cerr is another instance of the ostream class, and allows you to perform output to the stderr device.
  • istream and ostream aren't actually classes in their own right, they are typedefs (i.e. aliases) that represent character-based instantiations of the basic_istream and basic_ostream template classes

Manipulators

  • You can format the way output is displayed on a stream (minimum width for a value, alignment) by using manipulators
  • Manipulators are predefined objects that you pass to the stream, to obtain the formatting effect you desire. E.g. std::endl is a manipulator which tells the stream to output an end-of-line
  • Manipulators can take parameters
#include <iomanip>    // Necessary for parameterized manipulators.
#include <iostream>   // Necessary for general stream I/O definitions.
...
std::cout << std::left << std::fixed << std::setw(8) << -123.45 << std::endl;
std::cout << std::scientific << -123.45 << std::endl;
std::cout.unsetf(std::ios::fixed | std::ios::scientific);

Files

  • The standard C++ library defines three stream-based classes in <fstream>
    • std::ifstream inherits from std::istream, and allows you to read data from a file using the >> operator.
    • std::ofstream inherits from std::ostream, and allows you to write data to a file using the << operator.
    • std::fstream inherits from both std::istream and std::ostream, and allows you to read and write data to/from a file using the >> and << operators.
std::fstream  iofile("file2.dat", std::ios_base::binary | std::ios_base::app);

Lambda Expressions

  • In C++11, a lambda expression—often called a lambda is a convenient way of defining an anonymous function object right at the location where it is invoked or passed as an argument to a function
  • A lambda can introduce new variables in its body variables from the surrounding scope.

Elements:

  1. Capture clause (lambda-introducer)
  2. Parameter list Optional. (lambda declarator)
  3. Mutable specification Optional
  4. Exception-specification Optional
  5. Trailing-return-type Optional
  6. Lambda body

TODO add lambda example

Other C++ & General Programming Considerations

  • We use the inline keyword to define Inline functions which avoid the overhead associated with traditional function calls, because every time an inline function is called the compiler will insert the body of the function in that location as opposed to making a function call. However the inline keyword is a compiler directive that is a recommendation only. The compiler may ignore your request and compile the function normally resulting in function calls anyway.

  • Static class members are defined using the keyword static, if a member is declared static then it is shared by all the objects of the class. Likewise a static function member is independent of all the objects, they can be accessed using the class name and the scope operator ::. Static member functions can only access static data members, static functions and other functions outside the class. Usually declared in the class definition (.hpp) e.g. private: static int count; and initialized in the implementation file (.cpp) using the scope operator (::), e.g. int Counter::count = 0;

  • Ambiguity only leaves work to the compiler to define that ambiguity

  • the thread_local modifier was defined as part of the C++11 standard, it declares that a variable is only accessible on the thread in which it is created. This prevents sharing of the identifier across multiple threads in the same application.

  • The OS can use various methods to provide memory to an application when it requests more, such as swapping where it uses a portion of storage space on the fixed disk to move data and programs out of main memory and to the local disk, in order to permit a running application to have memory resources

  • The mutable keyword is used to declare that a data member can be assigned a value and it can only be applied to non static and non const members. In general all variables are mutable, an example usage would be if you have a const object but need to be able to modify one of its data members we can declare it mutable and then we are able to change its value

  • In C++11 we have the alignas and alignof keywords, alignas allows us to align a variable to a multiple number of bytes (which can make cache read/writes more efficient because when memory is read by the CPU it reads it in as multiples of word size) and alignof gives us alignment of a variable in bytes.

  • In general we cannot use the address operator (&) on a variable declared with keyword register because we have told the compiler to keep the variable in the register and not in the memory. Therefore, there is no memory address for the variable. However using the register keyword does NOT guarantee the compiler will keep the variable in the register

Questions / TODOs

  • A Shared library loads all lib in memory when calling classes functions?
  • What are the C++ primitives?
  • How to implement multiple undefined arguments functions/methods like printf

References

Want to show support?

If you find the information in this page useful and want to show your support, you can make a donation

Use PayPal

or

Use/Enable Monetization

Not Monetized

This will help me create more stuff and fix the existent content...