C++ Course

Lesson 5 recap

Objectives

- Copy Constructor

- Operator overloading

- friend

- Assignment operator

- cast operators

- inline

- Recursion

Copy constructor

class IntClass
{
  // There are 4 methods that are synthesised by the compiler 
  // when not explicitly coded by the developer.

  // Default constructor  - Constructor with no arguments
  // Destructor        

  // Copy constructor     - Constructor accepting as argument a const Reference
  // Assignment operator  
  
};

So each of the classes support the following behaviour

{
  Intclass a;       // Construction of an object
  Intclass a = b;   // Initialisation: Copy constructor is called
  Intclass a(b);    // Initialisation: Copy constructor is called

  a = b;            // Assignment of b to a. Assignment operator is called
}  // Destructor is called 


Copy constructor

A copy constructor is a constructor that accepts as argument 

A const reference of it's own type

class IntClass {
  IntClass(const IntClass& other);
};

It is used to initialise a new object of a class.

IntClass a;      // Default constructor
IntClass b(a);   // Copy constructor is called.
IntClass c(a);   // Copy constructor is called.


// NOTICE
a = b;           // This is not a copy constructor
                 // We do not initialise a 
                 // but we reassign a new value to a

Text

If a shallow copy is sufficient, the complier with generate a good enough copy constructor, otherwise, we are required to provide a correct implementation.

Operator overloading

If we have the following Celsius class

class Celsius 
{
    public:
    Celcius(float value): m_value(value) {}

    private:
    int m_value;
};

We would like to use the new type in an intuitive and neutral manner.

Celsius temp1 (37);
Celsius temp2 = temp1 + 5;

temp2 +=5;

cout << temp2 << endl;

if (temp2 < temp1) { /* do something */ }

Operator overloading

For this reason in C++ we can overload operator.

class Celsius 
{
    public:
    Celsius(float value): m_value(value) {}
 
    const Celsius& operator+=(const Celsius& other);     // a += b 
    Celsius        operator+ (const Celsius& other);     // a + b

    float getValue() const { return m_value; }
     
    private:
    int m_value;
};

ostream& operator<<(ostream& os, const Celsius& cel)
{
    return os << cel.getValue() << "C";
}

const Celsius& Celsius::operator+=(const Celsius& other) 
{ 
    m_value += other.m_value; 
    return *this; 
}

Celsius Celsius::operator+(const Celsius& other)
{
    Celsius temp = *this;
    return temp += other;
}

Operator overloading

Overloadable operators:

 

+ - * / % ^
& | ~ ! , =
< > <= >= ++ --
<< >> == != && ||
+= -= /= %= ^= &=
|= *= <<= >>= [] ()
-> ->* new new [] delete delete []

 

Operators that can't be overloaded

:: .* . ?:

Assignment operator

Assignment operator is very similar to copy constructor, with one distinction. Assignment operators is executed on an already initialised object, and requires to clean its previous state.

class Buffer {
  public:
  Buffer(int size):m_size(size), m_data(new char[size]) {} 
  Buffer(const Buffer& other): m_size(other.m_size), m_data(new char[m_size]) 
  { memcpy(m_data, other.m_data, m_size); }

  Buffer& operator=(const Buffer& other);
  ....
  private: 
     char*  m_data;
     size_t m_size;
};

Buffer& operator=(const Buffer& other) {
  if (other == *this)
    return *this; 

   delete [] m_data;

  m_size = other.m_size;
  m_data = new char[m_size]
  memcpy(m_data, other.m_data, m_size);

  return *this;
}

Assignment operator is required to also 

Clean it's previous state. & handle th

Protect against 

Buffer f(100);

f = f;

Return value is non-const reference to object

int a, b,c;      // Consider base type 

(a = b) = c;    // legal statement

Subscript operator []

Text

class SafeArray
{
   public:
     SafeArray(int size):m_size(size), m_data(new int[m_size]) {}  
     const int& operator[] (int index) const { return m_data[index]; }
           int& operator[] (int index) { return m_data[index]; }  

     ~SafeArray() { delete [] m_data; }
   private:
     int  m_size;
     int* m_data;
};


SafeArray  arr1(10);

arr1[1] = 10; // Non-const operator[] is called

const SafeArray arr2(10);
arr2[1] = 5;  // const operator[] is called on cost object. Won't compile 

Increment/decrement

increment (++) and decrement(--) operators can be prefixed or postfixed. 

              Postfixed                                                Prefixed 


//                 a        b 
int a(1);          1        
int b=a++;         2        1

//                 a        b 
int a(1);          1        
int b=++a;         1        1

//                 a        b 
int a(1);          1        
int b(a--);        0        1

//                 a        b 
int a(1);          1        
int b(--a);        0        0
class Time {
   public:
   ....                     // Notice the operators return value

   Time& operator++ () { ... }    // Overload prefix ++
   Time& operator++ (int) { ... } // Overload postfix ++

   Time operator-- () { ... }    // Overload prefix --
   Time operator-- (int) { ... } // Overload postfix --
};

Time t;

// Now all the following statement are valid
t++;  
++t;
t--;
--t;

Recursion

Recursive function is a function that calls itself

int factorial(int n)
{
    return n == 0 ? 1 : n * factorial(n-1);
}

/*
  factorial(5)
  5 * factorial(4) 
      4 * factorial(3)
          3 * factorial(2)
              2 * factorial(1)
                  1 * factorial(0)
                      1
         
*/
factorail(5); // 120  

In many cases recursion yields a much more elegant and intuitive code

Tail recusion

Recursion risk to get stack overflow, and slow run time a solution to that is tail recursion

int factorial_tail(int n, int acc=1)
{
    return (n == 0) ? acc : factorial_tail(n-1, n*acc);
}


/*
 Tail recursion version
 factorial(5,1)
 factorial(4,5)
 factorial(3,20)
 factorial(2,60)
 factorial(1,120)
 factorial(0,120)  return 120
*/ 
factorial_tail(5); // 120
Made with Slides.com