Module 02: Operator Overloading and Orthodox Canonical Form
Key Concepts:
- Orthodox Canonical Form (OCF)
- Copy constructor
- Copy assignment operator
- Operator overloading
- Fixed-point numbers
1. Orthodox Canonical Form (OCF)
Section titled “1. Orthodox Canonical Form (OCF)”The Four Required Members
Section titled “The Four Required Members”From Module 02 onwards, ALL classes must implement:
class Sample {public: Sample(); // 1. Default constructor Sample(const Sample& other); // 2. Copy constructor Sample& operator=(const Sample& other); // 3. Copy assignment operator ~Sample(); // 4. Destructor};Why OCF Matters
Section titled “Why OCF Matters”Without proper OCF, classes with dynamic memory will have bugs:
// BAD: No copy constructor or assignment operatorclass Bad {private: int* _data;public: Bad() { _data = new int(42); } ~Bad() { delete _data; }};
Bad a;Bad b = a; // Default copy: b._data = a._data (same pointer!)// When a and b are destroyed: double delete! CRASH!2. Copy Constructor
Section titled “2. Copy Constructor”Purpose
Section titled “Purpose”Creates a NEW object as a copy of an existing object.
class Sample {private: int _value; int* _data;
public: // Copy constructor Sample(const Sample& other) : _value(other._value) { std::cout << "Copy constructor called" << std::endl; // Deep copy: allocate new memory and copy content _data = new int(*other._data); }};When It’s Called
Section titled “When It’s Called”Sample a;Sample b(a); // Copy constructorSample c = a; // Copy constructor (NOT assignment!)func(a); // Copy constructor (pass by value)return a; // Copy constructor (return by value)Shallow vs Deep Copy
Section titled “Shallow vs Deep Copy”// SHALLOW COPY (default, dangerous with pointers)class Shallow { int* _ptr;public: Shallow(const Shallow& other) { _ptr = other._ptr; // Both point to same memory! }};
// DEEP COPY (correct for pointers)class Deep { int* _ptr;public: Deep(const Deep& other) { _ptr = new int(*other._ptr); // New memory, copy value }};3. Copy Assignment Operator
Section titled “3. Copy Assignment Operator”Purpose
Section titled “Purpose”Assigns the value of one EXISTING object to another EXISTING object.
class Sample {public: Sample& operator=(const Sample& other) { std::cout << "Copy assignment operator called" << std::endl;
// 1. Check for self-assignment if (this != &other) { // 2. Clean up existing resources delete _data;
// 3. Copy values _value = other._value; _data = new int(*other._data); }
// 4. Return *this for chaining: a = b = c; return *this; }};When It’s Called
Section titled “When It’s Called”Sample a;Sample b;b = a; // Assignment operator (b already exists)The Self-Assignment Check
Section titled “The Self-Assignment Check”Sample a;a = a; // Self-assignment - would be bug without check!
// Without check:// 1. delete _data; // Free the memory// 2. _data = new int(*other._data); // other._data is already deleted!4. Complete OCF Example
Section titled “4. Complete OCF Example”class Fixed {private: int _rawValue; static const int _fractionalBits = 8;
public: // Default constructor Fixed() : _rawValue(0) { std::cout << "Default constructor called" << std::endl; }
// Copy constructor Fixed(const Fixed& other) : _rawValue(other._rawValue) { std::cout << "Copy constructor called" << std::endl; }
// Copy assignment operator Fixed& operator=(const Fixed& other) { std::cout << "Copy assignment operator called" << std::endl; if (this != &other) _rawValue = other._rawValue; return *this; }
// Destructor ~Fixed() { std::cout << "Destructor called" << std::endl; }
// Getters/Setters int getRawBits() const { std::cout << "getRawBits member function called" << std::endl; return _rawValue; }
void setRawBits(int const raw) { _rawValue = raw; }};5. Operator Overloading
Section titled “5. Operator Overloading”What is Operator Overloading?
Section titled “What is Operator Overloading?”Defining custom behavior for operators (+, -, *, /, ==, <, <<, etc.) when used with your class.
Syntax
Section titled “Syntax”// As member functionReturnType operator@(parameters);
// As non-member function (friend or uses public interface)ReturnType operator@(LeftType, RightType);Comparison Operators
Section titled “Comparison Operators”class Fixed {public: bool operator>(const Fixed& other) const { return _rawValue > other._rawValue; }
bool operator<(const Fixed& other) const { return _rawValue < other._rawValue; }
bool operator>=(const Fixed& other) const { return _rawValue >= other._rawValue; }
bool operator<=(const Fixed& other) const { return _rawValue <= other._rawValue; }
bool operator==(const Fixed& other) const { return _rawValue == other._rawValue; }
bool operator!=(const Fixed& other) const { return _rawValue != other._rawValue; }};Arithmetic Operators
Section titled “Arithmetic Operators”class Fixed {public: Fixed operator+(const Fixed& other) const { Fixed result; result._rawValue = _rawValue + other._rawValue; return result; }
Fixed operator-(const Fixed& other) const { Fixed result; result._rawValue = _rawValue - other._rawValue; return result; }
Fixed operator*(const Fixed& other) const { Fixed result; // For fixed-point: (a * b) >> fractionalBits result._rawValue = (_rawValue * other._rawValue) >> _fractionalBits; return result; }
Fixed operator/(const Fixed& other) const { Fixed result; // For fixed-point: (a << fractionalBits) / b result._rawValue = (_rawValue << _fractionalBits) / other._rawValue; return result; }};Increment/Decrement Operators
Section titled “Increment/Decrement Operators”class Fixed {public: // Pre-increment: ++a Fixed& operator++() { _rawValue++; return *this; }
// Post-increment: a++ Fixed operator++(int) { // int parameter is just a marker Fixed temp(*this); // Save current value _rawValue++; // Increment return temp; // Return old value }
// Pre-decrement: --a Fixed& operator--() { _rawValue--; return *this; }
// Post-decrement: a-- Fixed operator--(int) { Fixed temp(*this); _rawValue--; return temp; }};Stream Insertion Operator (<<)
Section titled “Stream Insertion Operator (<<)”// Must be non-member function (ostream is on the left)std::ostream& operator<<(std::ostream& os, const Fixed& fixed) { os << fixed.toFloat(); return os;}
// UsageFixed a(42.42f);std::cout << a << std::endl; // Prints: 42.42196. Fixed-Point Numbers
Section titled “6. Fixed-Point Numbers”What are Fixed-Point Numbers?
Section titled “What are Fixed-Point Numbers?”A way to represent decimal numbers using integers, with a fixed number of bits for the fractional part.
Example: 8 Fractional Bits
Section titled “Example: 8 Fractional Bits”Integer: 42Binary: 00101010.00000000 ^^^^^^^^ ^^^^^^^^ Integer Fraction
To convert:- Int to Fixed: int << 8- Fixed to Int: fixed >> 8- Float to Fixed: float * 256 (then round)- Fixed to Float: fixed / 256.0fImplementation
Section titled “Implementation”class Fixed {private: int _rawValue; static const int _fractionalBits = 8;
public: // From int Fixed(const int value) : _rawValue(value << _fractionalBits) {}
// From float Fixed(const float value) : _rawValue(roundf(value * (1 << _fractionalBits))) {}
// To int int toInt() const { return _rawValue >> _fractionalBits; }
// To float float toFloat() const { return (float)_rawValue / (1 << _fractionalBits); }};Why Fixed-Point?
Section titled “Why Fixed-Point?”- Faster than floating-point on some hardware
- Predictable precision
- No floating-point rounding errors for exact values
- Used in: graphics, audio, embedded systems
7. Static Member Functions
Section titled “7. Static Member Functions”min and max Functions
Section titled “min and max Functions”class Fixed {public: // Non-const version static Fixed& min(Fixed& a, Fixed& b) { return (a < b) ? a : b; }
// Const version static const Fixed& min(const Fixed& a, const Fixed& b) { return (a < b) ? a : b; }
// Non-const version static Fixed& max(Fixed& a, Fixed& b) { return (a > b) ? a : b; }
// Const version static const Fixed& max(const Fixed& a, const Fixed& b) { return (a > b) ? a : b; }};
// UsageFixed a(2.0f), b(3.0f);std::cout << Fixed::max(a, b) << std::endl; // 3Quick Reference
Section titled “Quick Reference”OCF Template
Section titled “OCF Template”class ClassName {public: ClassName(); // Default constructor ClassName(const ClassName& other); // Copy constructor ClassName& operator=(const ClassName& other); // Assignment operator ~ClassName(); // Destructor};Operator Overloading
Section titled “Operator Overloading”| Operator | Member? | Signature |
|---|---|---|
+, -, *, / | Yes | T operator+(const T&) const |
==, !=, <, > | Yes | bool operator==(const T&) const |
++, -- (pre) | Yes | T& operator++() |
++, -- (post) | Yes | T operator++(int) |
<< | No | ostream& operator<<(ostream&, const T&) |
Fixed-Point Conversions
Section titled “Fixed-Point Conversions”// 8 fractional bitsint_to_fixed: value << 8fixed_to_int: value >> 8float_to_fixed: roundf(value * 256)fixed_to_float: value / 256.0f