# 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)
---