# Orthodox Canonical Form Mastering the **Orthodox Canonical Form** is essential for writing solid C++ classes. This form ensures your class behaves predictably, especially when **copied** or **assigned**. --- ### [Ref] CPP-ex00 Let me walk you through the **best practice implementation** of a class following this form. We'll focus on a class called `Fixed`, and we’ll: - Follow **Orthodox Canonical Form** (aka **Rule of Three**), - Use **initialization lists**, - Ensure **safe copying and assignment**, - Include **clear, maintainable logging** (optional but useful when learning). --- ## βœ… Orthodox Canonical Form β€” The "Rule of Three" A class that **manages resources** (like dynamic memory, file handles, etc.), or **encapsulates internal state**, should implement: 1. A **default constructor** 2. A **copy constructor** 3. A **copy assignment operator** 4. A **destructor** Even if it doesn't manage memory directly, this pattern is a **best practice** for clarity and maintainability. --- ## πŸ“¦ Fixed.hpp ```cpp #ifndef FIXED_HPP #define FIXED_HPP #include <iostream> class Fixed { private: int _value; // Internal fixed-point value static const int _fractionalBits = 8; // Number of fractional bits (e.g., 8 for 256 precision) public: Fixed(); // Default constructor Fixed(const Fixed& other); // Copy constructor Fixed& operator=(const Fixed& other); // Copy assignment operator ~Fixed(); // Destructor int getRawBits() const; // Getter void setRawBits(int const raw); // Setter }; #endif ``` --- ## πŸ”§ Fixed.cpp ```cpp #include "Fixed.hpp" // Default constructor Fixed::Fixed() : _value(0) { std::cout << "Default constructor called" << std::endl; } // Copy constructor Fixed::Fixed(const Fixed& other) : _value(other._value) { std::cout << "Copy constructor called" << std::endl; } // Copy assignment operator Fixed& Fixed::operator=(const Fixed& other) { std::cout << "Copy assignment operator called" << std::endl; if (this != &other) { this->_value = other._value; } return *this; } // Destructor Fixed::~Fixed() { std::cout << "Destructor called" << std::endl; } // Getter int Fixed::getRawBits() const { std::cout << "getRawBits member function called" << std::endl; return this->_value; } // Setter void Fixed::setRawBits(int const raw) { this->_value = raw; } ``` --- ## βœ… Key Best Practices You’re Using | Practice | Why It Matters | |---------|----------------| | **Initialization Lists** | Avoids default construction + reassignment. More efficient. | | **Copy Constructor / Assignment Operator** | Enables proper copying. | | **Self-assignment check** | Prevents bugs when assigning `a = a`. | | **Destructor even if trivial** | Future-proof. Add custom cleanup if needed. | | **Encapsulation (`private _value`)** | Keeps internal state safe and controlled. | | **Static const member (`_fractionalBits`)** | Shared constant for all instances; good for precision configuration. | --- ## 🧠 Pro Tip: When to Use Rule of Five / Zero? You used the **Rule of Three** β€” great start. - Use **Rule of Five** if your class **allocates memory or manages external resources**, like using `new`, file handles, etc. Then you also need: - Move constructor - Move assignment operator - Use **Rule of Zero** when your class has **no resource management** and just uses smart pointers or standard types like `std::vector`. --- ## The best to init variables in C++ common practice? ### βœ… **Best Practice: Use Initialization List in the Copy Constructor** It is the **preferred professional standard** in C++. **initializing members directly in the initializer list** is the preferred and most efficient way. Here’s why: --- ### πŸ” Why Initialization List Is Better ```cpp // Preferred Fixed::Fixed(const Fixed& other) : _value(other._value) { std::cout << "Copy constructor called" << std::endl; } ``` ### ❌ Instead of This (Less Ideal) ```cpp Fixed::Fixed(const Fixed& other) { std::cout << "Copy constructor called" << std::endl; _value = other._value; // assignment after default construction } ``` --- ### βœ… Benefits of Using Initialization List | Advantage | Explanation | |----------|-------------| | **Performance** | The member is constructed directly with the desired value β€” no temporary default value, no reassignment. | | **Safety** | Ensures constant or reference members (if any) are initialized properly (they *must* be initialized in the list). | | **Clarity** | Expresses intent: you're *initializing*, not *reassigning*. | | **Consistency** | Aligns with how built-in types and classes like `std::string`, `std::vector`, etc., are initialized safely. | --- ### 🧠 Rule of Thumb > **Always use initializer lists in constructors, especially for:** > - Member variables of class or struct types > - `const` members > - References > - Large or complex types (for performance) ---