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

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


Prep/Setup

  1. Ubuntu Linux install gcc and g++
 sudo add-apt-repository ppa:ubuntu-toolchain-r/test
 sudo apt update
 sudo apt install gcc-11 g++-11
 gcc-11 --version
  1. Test Hello world
 // g++-11 main.cpp -o main && ./main

 #include <iostream>

 int main() {
     std::cout << "Hello World C++!\n";
 }

C++ basic compilation & template

PNAME="p1testcpp"; cd .; mkdir -p $PNAME/src;
printf '#include <iostream>\nusing namespace std;\n\nint main(){\ncout << "Hello Template" << endl;\nreturn 0;\n}\n' > $PNAME/src/main.cpp
g++ -Wall -std=c++14 main.cpp -o main && ./main
  • Simple C++ Makefile
TARGET=main.out
SRCS:=main.cpp

CC = g++-11
CFLAGS = -Wall -Werror
OBJS = $(SRCS:.cpp=.o)
%.o : %.cpp
    $(CC) -c $< $(CFLAGS) -o $@
.PHONY: all
all: $(TARGET)
$(TARGET): $(OBJS)
    $(CC) $(CFLAGS) -o $(TARGET) $(OBJS)
.PHONY: clean
clean:
    rm -rf $(OBJS) $(TARGET)
.PHONY: test
test: clean all
    ./$(TARGET)

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.

  • C++ uses value semantics by default so initialization creates a new objects that exists within a scope but allows reference semantics

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)

Fundamental Types

Fundamental Types

  • You can use the #include <climits> library for size and precision when declaring primitive data types

Enums & Constants

  • 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 };

  • To declare constants in C++ we can use const & constexpr

std::str & vectors

  • std::string class
    • It is a dynamic array that has data pointer and n for the number of lements (size() returns n)
    • Although they allocate dynamic memory on heap std:string objects can be used like local variables std::string h{"Hello"};
    • they are reallocated in memory when needed, copy and assignment are handled automatically
  • std::vector class
    • Similar to std::string but can store data of any type
    • size() returns number of elements and push_back() add element at the end
    • Multidimensional arrays are stored as as single memory block, row 1 store fisrt then second row and so on

Variable Initialization and Types

  • 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
    #include <iostream>
    #include <climits>
    #include <vector>
    //typedef std::vector<int> IntVec; // alias old C and C++
    using IntVec = std::vector<int>;// Modern C++
    class MyClass {};
    int main() {
      int myVar = 0;
      int yourVar{1};
      bool flag{ true };
      int zeroVar{};
      double myDouble(53.53);
      vector<int> vec{1,2,3,4,5,6};
      int intArray[] {1,2,4,6};
      string s{"My String"}; // sames as: string s("My String");
    
      std::vector<int> old_one(4);    // std::vector variable with elements 0, 0, 0, 0
      std::vector<int> old_two(4, 2); // std::vector variable with elements 2, 2, 2, 2
      std::vector<int> uni_one{4};    // std::vector variable with elements 4
      std::vector<int> uni_two{4, 2}; // std::vector variable with elements 4, 2
      std::vector<IntVec> vec_of_vec; 
    
      MyClass mc{}; // removes ambiguity, it doesnt look like a func declaration 
    
      bool a = true;
      char b = 'b';
      signed char c = -1;
      signed char d = 64;
      short e = -16384;
      unsigned short f = 16384;
      int g = -2;
      unsigned int h = 32768;
      long int i = -32768;
      long unsigned int j = -32768;
      long long int k = -4294967296;
      long long unsigned int l = 4294967296;
      float m = 3.141615;
      double n = 3.141615;
      const char *str = "Hello"; // Equivalent to const char str[] = {'H','e','l','l','o','\0'}
    
      std::cout << "bool " << a << " sizeof " << sizeof(a) << "\n";
      std::cout << "char " << b << " sizeof " << sizeof(b) << "\n";
      std::cout << "signed char " << c << " sizeof " << sizeof(c) << "\n";
      std::cout << "signed char " << d << " sizeof " << sizeof(d) << "\n";
      std::cout << "short " << e << " sizeof " << sizeof(e) << "\n";
      std::cout << "unsigned short " << f << " sizeof " << sizeof(f) << "\n";
      std::cout << "int " << g << " sizeof " << sizeof(g) << "\n";
      std::cout << "unsigned int " << h << " sizeof " << sizeof(h) << "\n";
      std::cout << "long int " << i << " sizeof " << sizeof(i) << "\n";
      std::cout << "long unsigned int " << j << " sizeof " << sizeof(j) << "\n";
      std::cout << "long long int " << k << " sizeof " << sizeof(k) << "\n";
      std::cout << "long long unsigned int " << l << "sizeof " << sizeof(l) << "\n";
      std::cout << "float " << m << " sizeof " << sizeof(m) << "\n";
      std::cout << "double " << n << " sizeof " << sizeof(n) << "\n";
      //std::cout << "Square auto func " << square(g) << "\n";
    
      std::cout << "char MIN "  << CHAR_MIN << "\n";
      std::cout << "int MIN " << INT_MIN << "\n";
      std::cout << "short MIN "  << SHRT_MIN << "\n";
      std::cout << "long MIN "  << LONG_MIN << "\n";
      std::cout << "long long MIN " << LLONG_MIN << "\n";
    
      std::cout << "char MAX "  << CHAR_MAX << "\n";
      std::cout << "int MAX " << INT_MAX << "\n";
      std::cout << "short MAX "  << SHRT_MAX << "\n";
      std::cout << "long MAX "  << LONG_MAX << "\n";
      std::cout << "long long MAX " << LLONG_MAX << "\n";
    
      // The comma operator has left-to-right associativity. It evaluates the left expression,
      // discards its result, and returns the right expression
      int x = (g++, 6);
      std::cout << "Comma operator test x=" << x << " g=" << g << "\n";
      /*
      Comma operator test x=6 g=-1
      */
    
      // C++20 three-way comparison operator <=> a.k.a. 'spaceship operator'
      // operator compares alphabetical order like strcmp
      // operator returns an object that can be directly compared with a positive, 0, or negative integer value
      std::cout << "Spaceship operator test " << ((3 <=> 5) == 0) << "\n";
      std::cout << "Spaceship operator test " << ((3 <=> 5) < 0) << "\n";
      std::cout << "Spaceship operator test " << ((3 <=> 5) > 0) << "\n";
      
      /*
      Spaceship operator test 0
      Spaceship operator test 1
      Spaceship operator test 0
      */
    
      // C++20 introduces a set of safe compare functions <utility>
      // bool cmp_equal(T1 a, T2 b)
      // bool cmp_not_equal(T1 a, T2 b)
      // bool cmp_less(T1 a, T2 b)
      // bool cmp_greater(T1 a, T2 b)
      // bool cmp_less_equal(T1 a, T2 b)
      // bool cmp_greater_equal(T1 a, T2 b)
      unsigned o = 4;
      int p = -3;
      std::cout << "Without utility " << (o > p) << " With utility " << std::cmp_greater(o,p) << "\n";
      /* Without utility 0 With utility 1 */ 
    }

  • C++17 Allows initiliazer in an if and switch statements
    • if (auto iter = begin(vec) ; iter != end(vec)) varible is local to if-else statement
    • switch (char c = arr[i]; c)

C++ components

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

Literals

  • Numeric literals are double and int by default you can use modifiers like f and UL to change this
  • String C-string literal const char *cca="Hello" is null terminated array of const char which has limited functionality, for pure C++ string objects are recommended
  • C++11 supports raw string, needs character R and () if string has () needs an extra delimiter. Examples:
    • std::string url = R"(<a href="file">C:\Program Files\</a>\n)";
    • std::string delimited_with_x_url = R"x(<a href="file">C:\"Program Files (x86)"\</a>\n)x";

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)
  • A struct in C++ is the same as class, except all the members are public by default

When you call a member function (of an object), the object is passed by address in a hidden argument called this which you can deferecne to access the members of the object

class Test {
  int i;
  std::string s;
  public:
  func(int a, float b, std::string s);
};

void Test::fun(int a, float b, std::string s) {
  this->i = 1; //  'this' is equal to &test (the address of test) 
}

Test test;
test.func(1, 2.0, "Three"); // Is called as Test::func(&test, 1, 2.0,"Three");

Special member functions

  • Compiler automatically insert calls to special member functions when needed fot the for the management of objects
  • Special member functions in C++: Constructor, Copy Constructor, Assigment Operator, Destructor

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{}; 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

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

Copy Constructors

  • 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); This is the most common to avoid changing something on the object you are copying
    2. MyClass(MyClass& other);
    3. MyClass(volatile const MyClass& other);
    4. MyClass(volatile MyClass& other);
class Test {
  int i;
  std::string s;
  public:
  func(int a, float b, std::string s);
};

Test(const Test& other) : i(other.i), str(other.str) {

}

Overloading Assignment Operator (Equals operator = with objects)

  • 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
  • If we want to ensure a behavior defined by us qhen we use the = to assign object we must overload the operator accordingly or explicitly implement the copy constructor with our code.
  • For overloading it must always be defined to take one argument, which is a reference to another object of the same class (this will be the object that will on the right of the = operator)
  • Operator returns a reference to the assignd object so chains of operators can work
  • E.g. .hpp: MyClass& operator =(const MyClass &rhs); .cpp: MyClass& MyClass::operator =(const MyClass &rhs)
class Test {
  int i;
  std::string s;
  public:
  func(int a, float b, std::string s);
};

Test& operator=(const Test& other_on_the_right_side) {
  i = other.i;
  str = other.str;
  return *this;
}
// 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

  • They are like constant pointer that is automatically dereferenced when you use it

  • Mostly used for

    • Function/Method parameters
    • Range based auto loops if we want to perform changes to the elements on the loop
  • 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 to a variable when declared
  • Pass by const reference

    • So a function/method can only call const public methods and members
    • The thing that's being referred to can't be changed
    • gives read-only access to an object members/methods
// 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
  
  vector<string> names {"anna", "ben", "camila"};
  for (auto const &str:names)
    cout << str << "\n"; // no change
  for (auto &str:names)
    str = "noname"; // change
}

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

void display(const vector<string> *const v) {
    // (*v).at(0) = "Name"; //const to prevent this
    for (auto e: *v) {
        cout << e << "";
        cout << endl;
    }
    //v = nullptr; //*const to prevent this 
}

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, default 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
    3. dangling pointer The pointer variable that points to the released memory will still exist on the stack until the end of it's scope
  • 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

int *p1 = new int{36};
int *p2 = new int(37);

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

The OS restricts the amount of memory a program may allocate if try to locate over this resctriction most likely will get a nullptr (git a Memory exhaustion failur)

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))
{
 . . .
}

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 : <inheritance_type> 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

  • C++ cannonical definition of casting <cast_type><variable_type>(variable)
  • 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<MyBase*>(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. Also sometimes needed to call a method/function that doesnt use const and you want to pass a const
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. Used to convert data ina buffer to untyped binary data
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 <char*>(obj);    // equivalent to obj.operator char*()
static_cast <double>(obj);    // equivalent to obj.operator double() or (double) obj;

double xd = 2.241234;
int xi = static_cast<int>(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 runtime 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
  • Converts pointers of base calls to a pointer of a derived class
  • 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* dynPtr;

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

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

auto

  • In pure C it used to explcitly singal that a variable should be created on the stack and currently by default all local variables in C are auto vars
  • The keyword auto is used to let the compiler determine the appropriate type for a variable based on the context of it's initialization (type inference)
  • auto ignores const and reference qualifier, if a reference is assigned to an auto variable a copy is created and assigned to the auto var
  • if you need const and reference qualifiers you can add them explicitly
  • Should be used when type does not provide useful info or code is just clearer without the type (i.e. for loops with complex types or iterators)
auto str1 = "Hello";  // equivalent const char str1[] = "Hello"
auto str2 = "Hello"s; // equivalent std::string char str1 = "Hello"

const int& x{6};
auto y=x; 
y++; //y==7 x==6 at this point

const auto& y = x;

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`

lvalues & rvalues

  • An lvalue is an object/variable that have a name, occupies a location in memory and is addresable
  • An rvalue literals and other things that generate compiler error when used on left side. They are non-addresable and you cannot assign them new values but can be assigned to l-values
 int x {100}; // 100 is an r-value and x is an l-value
 int y = x + 100 // (x+100) is an r-value and y is an l-value
 int m = max(x,y) // max(x,7) is an r-value and m is an l-value
 
 int square (int &n) {
   return n*n;
 }
 int num {10};
 square(num); // OK
 square(5); // Error can't reference rvalue 5
 

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<int, double, 3> 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

  • We use the form <type>::iterator to declare an interator where type is the typo of the container

  • They are basically pointers so we use the * to derefence current element pointed by an interator

  • We use the begin() & end() methods of a container to get an iterator to the first element and to iterate we use a while loop comparing if begins is less/diffrent from end and increment begin

  • 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>

using namespace std;

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[]) {
    string str = "Hello";
    for (string::iterator itStr = str.begin(); itStr != str.end();  itStr++){
      cout << *itStr << ",";
    }
    cout << "\n\n";

    list<int> List = { 34, 77, 16, 2, 35, 76, 18, 2 };
    cout << "Calling sort on vector " << "\n";
    List.sort();
    for (auto it: List) {
        cout << it << " ";
    }
    cout << "\n\n";
    cout << "Results after calling randomise are " << "\n";
    vector<int> temp(List.begin(), List.end()); // Using constructor that basically builds a new vector of ints
    randomise(temp.begin(),temp.end());
    for (vector<int>::iterator it = temp.begin(); it != temp.end(); it++) {
        cout << *it << " ";
    }
    
    cout << "\n\n";
    cout << "Print orig list after randomize " << "\n";
    for (auto it: List) {
        cout << it << " ";
    }
}

Types of Iterators

  • const iterator prevent a loop from modifying elements or if you have a const container.
  • reverse iterator iterate backwards
  • cbegin(), cend(), crbegin() & crend() return const iteratros and can be used with auto
  • C++11 provides begin() and end() global functions
#include <iostream>

using namespace std;

int main(int argc,char* argv[]) {
  string str = "Hello";
  
  // const iterator
  string::const_iterator cit;
  for (cit=str.begin(); cit!=str.end(); ++cit)
    cout << *cit << ",";
  cout << "\n\n";

  // reverse iterator
  string::reverse_iterator rit;
  for (rit=str.rbegin(); rit!=str.rend(); ++rit)
    cout << *rit << ",";
  cout << "\n\n";

  // cbegin cend
  for (auto it=str.cbegin(); it!=str.cend(); ++it)
    cout << *it << ",";
  cout << "\n\n";

  // global begin end
  for (auto it=begin(str); it!=end(str); ++it)
    cout << *it << ",";
  cout << "\n\n";
}

range for loops

  • Provide a special, concise syntax for iterating over containers.
  • Changes made to element in range loop do not affect the container, elements are a copy of the current element
  • To modify elements we need an auto reference auto&
  for (auto e: str)
    cout << e << ","; // cannot modify `str`. `e` is a copy of the element
  cout << "\n\n";

  for (auto& e: str){
    e++; // can modify `str`. `e` is a reference to the element
    cout << e << ","; 
  }
  cout << "\n\n";

Iterator Arithmetics

  • Add: auto second = begin(str)+1; // it to seconds element
  • Sub: auto last = end(str)-1; // it to last element
  • Diff: auto mid = begin(str)+(end(str)-begin(str))/2; // it to mid element since end(str)-begin(str) gives the number of elements which can be divided 2
  • next takes an interator returns following iterator: auto second = next(begin(str));
  • prev takes an interator returns previous iterator: auto last = prev(end(str));
  • distance returns number of steps to go from first to second argument distance(begin(str),end(str)); // Returns number of elements
  • advance moves an iterator by its second argument auto mid = being(str); advance(mid,distance(begin(str),end(str))/2); // Returns number of elements
  • Half open range: meaning it >= begin() and it < end() must be true but it==end() is not allowed

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

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