Understanding the Orthodox Canonical Class Form
The Orthodox Canonical Form in C++ involves defining 5 special member functions for a class.
- Default Constructor
- Parameterized Constructor
- Copy Constructor
- Assignment Operator
- Destructor
Considering the following Human
class, let’s talk about each of these member functions.
class Human {
private:
std::string _name;
int _age;
public:
const std::string &getName() const {
return (_name);
}
int getAge() const {
return (_age);
}
};
Default Constructor
The Default Constructor is a special member function in a class that initializes an object with default values during the object’s instantiation.
class Human {
private:
...
public:
Human() : _name("Default Name"), _age(0) {
std::cout << "Human Default Constructor Called!" << std::endl;
}
...
};
Usage:
int main(void) {
Human h1;
std::cout << h1.getName() << std::endl;
std::cout << h1.getAge() << std::endl;
return (0);
}
Parameterized Constructor
The Parameterized Constructor initializes an object with specific values provided as arguments during the object’s instantiation.
class Human {
private:
...
public:
Human(const std::string &name, int age) : _name(name), _age(age) {
std::cout << "Human Parameterized Constructor Called!" << std::endl;
}
...
};
Usage:
int main(void) {
Human h1("Mark", 42);
std::cout << h1.getName() << std::endl;
std::cout << h1.getAge() << std::endl;
return (0);
}
Copy Constructor
The Copy Constructor initializes a new object as a copy of an existing object. This is useful when passing an object by value or when we need to duplicate an object.
class Human {
private:
...
public:
Human(const Human &other) : _name(other._name), _age(other._age) {
std::cout << "Human Copy Constructor Called!" << std::endl;
}
...
};
Usage:
int main(void) {
Human h1("Mark", 42);
Human h2(h1);
std::cout << h2.getName() << std::endl;
std::cout << h2.getAge() << std::endl;
return (0);
}
Assignment Operator
The Assignment Operator assigns the value of one object to another already-existing object. Here, we need to handle deep copying and self-assignment.
class Human {
private:
...
public:
Human &operator=(const Human &other) {
if (this != &other) {
_name = other._name;
_age = other._age;
}
std::cout << "Human Assignment Operator Called!" << std::endl;
return (*this); //Required for chaining
}
...
};
Note: this
is a pointer of type Human *
which points to the current object. Dereferencing it gives us access to the current object. If the assign operator gets called like this h2 = h1
then, h1
refers to other
and h2
refer to this
in this case.
Usage:
int main() {
Human h1("Mark", 42);
Human h2("John", 30);
h2 = h1;
std::cout << h2.getName() << std::endl;
std::cout << h2.getAge() << std::endl;
return (0);
}
Destructor
The Destructor is called when an object goes out of scope or is explicitly deleted. It is used to clean up resources such as memory or file handles.
class Human {
private:
...
public:
Human(const std::string& name, int age) : _age(age) {
_name = new std::string(name); // Dynamic Memory Allocation
}
~Human() {
std::cout << "Human Destructor Called!" << std::endl;
delete _name; // Clean Up
}
...
};