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
C++L05
By perplexedpigmy
C++L05
- 766