Skip to content

Instantly share code, notes, and snippets.

@SteelPh0enix
Created July 6, 2023 20:23
Show Gist options
  • Save SteelPh0enix/11a054db32d257072b7ddfc69ed930bf to your computer and use it in GitHub Desktop.
Save SteelPh0enix/11a054db32d257072b7ddfc69ed930bf to your computer and use it in GitHub Desktop.
Rule of Five
#include <iostream>
#include <string>
template <typename T>
class Container {
public:
// Prevents creating nameless objects without value - assume that these are invalid.
Container() = delete;
Container(T const value, std::string const& name) : m_name { name }
{
create_value(value);
std::cout << "Value c-tor called, created object '" << this->name() << "' with value '"
<< this->value() << "'\n";
}
Container(Container const& other) : m_name { other.m_name }
{
create_value(*other.m_value);
std::cout << "Copy c-tor called, copied object '" << name() << "' with value '" << value()
<< "'\n";
}
Container(Container&& other) : m_value { other.m_value }, m_name { std::move(other.m_name) }
{
std::cout << "Move c-tor called, moved object '" << name() << "' with value '" << value()
<< "'\n";
// Invalidate members of moved instance to preserve object cohesion
// `name` has already been invalidated by std::string's move c-tor
other.m_value = nullptr;
}
Container& operator=(Container const& other)
{
m_name = other.m_name;
// Assume pessimistic scenario - previous data must be destroyed, cannot be reassigned
destroy_value();
create_value(*other.m_value);
std::cout << "Copy operator called, copied object '" << name() << "' with value '"
<< value() << "'\n";
return *this;
}
Container& operator=(Container&& other)
{
m_name = std::move(other.m_name);
m_value = other.m_value;
// Invalidate members of moved instance to preserve object cohesion
// `name` has already been invalidated by std::string's move c-tor
other.m_value = nullptr;
std::cout << "Move operator called, moved object '" << name() << "' with value '" << value()
<< "'\n";
return *this;
}
~Container()
{
if (is_valid()) {
std::cout << "Destructor called, destroying object '" << name() << "' with value '"
<< value() << "'\n";
destroy_value();
} else
std::cout << "Destructor called, destroying invalid object (no dealloc performed)\n";
}
std::string const& name() const
{
return m_name;
}
T const& value() const
{
return *m_value;
}
bool is_valid() const
{
return m_value != nullptr;
}
std::string to_string() const
{
if (is_valid())
return name() + std::string(" contains ") + std::to_string(value());
return "Invalid object!";
}
private:
T* m_value { nullptr };
std::string m_name {};
static inline size_t m_allocations { 0u };
static inline size_t m_deallocations { 0u };
void create_value(T const new_value)
{
m_value = new T { new_value };
m_allocations++;
std::cout << "Value allocated, performed " << m_allocations << " allocations\n";
}
void destroy_value()
{
delete m_value;
m_deallocations++;
std::cout << "Value deallocated, performed: " << m_deallocations << " deallocations\n";
}
};
int main()
{
std::cout << "=== Creating 'a' ===\n";
auto const a = Container(42, "First");
std::cout << "a = " << a.to_string() << '\n';
std::cout << "\n=== Creating 'b' ===\n";
auto b = Container(69, "Second");
std::cout << "b = " << b.to_string() << '\n';
std::cout << "\n=== Creating 'c' by copying 'a' ===\n";
auto c = a;
std::cout << "a = " << a.to_string() << '\n';
std::cout << "c = " << c.to_string() << '\n';
std::cout << "\n=== Creating 'd' by moving 'b' ===\n";
auto d = std::move(b);
std::cout << "b = " << b.to_string() << '\n';
std::cout << "d = " << d.to_string() << '\n';
std::cout << "\n=== Final state of all objects ===\n";
std::cout << "a = " << a.to_string() << '\n';
std::cout << "b = " << b.to_string() << '\n';
std::cout << "c = " << c.to_string() << '\n';
std::cout << "d = " << d.to_string() << '\n';
std::cout << "\n=== End of main ===\n";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment