Lesson 5 recap
- Copy Constructor
- Operator overloading
- friend
- Assignment operator
- cast operators
- inline
- Recursion
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
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.
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 */ }
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;
}
Overloadable operators:
+ | - | * | / | % | ^ |
& | | | ~ | ! | , | = |
< | > | <= | >= | ++ | -- |
<< | >> | == | != | && | || |
+= | -= | /= | %= | ^= | &= |
|= | *= | <<= | >>= | [] | () |
-> | ->* | new | new [] | delete | delete [] |
Operators that can't be overloaded
:: | .* | . | ?: |
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
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 (++) 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;
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
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