Defining Constructors
Rules of overloading default constructors and operators
Rule of 0
Classes have neither custom destructors, copy/move constructors or copy/move assignment operators.`
Rule of 3
If you implement a custom version of any of these, you implement all of them.
Destructor, Copy constructor, copy assignment
Rule of 5
If you implement a custom move constructor or the move assignment operator, you need to define all 5 of them. Needed for move semantics.
Destructor, Copy constructor, copy assignment, move constructor, move assignment
Rule of four and a half
Same as Rule of 5 but with copy and swap idiom. With the inclusion of the swap method, the copy assignment and move assignment merge into one assignment operator.
Destructor, Copy constructor, move constructor, assignment, swap (the half part)
Example (Rule of 5)
#include <iostream>
#include <cstring>
class String {
public:
// Constructor
String(const char* str = "") {
size = std::strlen(str);
data = new char[size + 1];
std::strcpy(data, str);
}
// Destructor
~String() {
delete[] data;
}
// Copy Constructor
String(const String& other) {
size = other.size;
data = new char[size + 1];
std::strcpy(data, other.data);
}
// Copy Assignment Operator
String& operator=(const String& other) {
if (this != &other) {
delete[] data; // Free existing resource
size = other.size;
data = new char[size + 1];
std::strcpy(data, other.data);
}
return *this;
}
// Move Constructor
String(String&& other) noexcept : data(other.data), size(other.size) {
other.data = nullptr; // Leave other in a valid state
other.size = 0;
}
// Move Assignment Operator
String& operator=(String&& other) noexcept {
if (this != &other) {
delete[] data; // Free existing resource
data = other.data;
size = other.size;
other.data = nullptr; // Leave other in a valid state
other.size = 0;
}
return *this;
}
private:
char* data;
size_t size;
};
Example (Rule of 4.5 - Using copy and swap Idiom)
#include <iostream>
#include <cstring>
#include <utility> // for std::swap
class String {
public:
// Constructor
String(const char* str = "") {
size = std::strlen(str);
data = new char[size + 1];
std::strcpy(data, str);
}
// Destructor
~String() {
delete[] data;
}
// Copy Constructor
String(const String& other) : data(nullptr), size(0) {
*this = other; // Use copy assignment
}
// Copy Assignment Operator
String& operator=(String other) {
swap(other); // Use the swap idiom
return *this;
}
// Move Constructor
String(String&& other) noexcept : data(other.data), size(other.size) {
other.data = nullptr; // Leave other in a valid state
other.size = 0;
}
// Move Assignment Operator
String& operator=(String&& other) noexcept {
swap(other); // Use the swap idiom
return *this;
}
// Swap function
void swap(String& other) noexcept {
std::swap(data, other.data);
std::swap(size, other.size);
}
private:
char* data;
size_t size;
};