Rule of five

Step through the destructor, copy and move of a C++ linked list.

Everything is simulated in this browser; nothing is sent to any server.

Operations

Call the constructor

Call the constructor

l1Not built
∅
  • Current
  • Pointer

Step 1 / 26

l1
l2
l3

Code

#include <iostream>
#include <utility>
class Nodo { // A node holds the data and a pointer to the next one
private: // Private attributes: only the methods touch them
int dato_;
Nodo* siguiente_;
public:
Nodo(int dato, Nodo* siguiente = nullptr) {
dato_ = dato;
siguiente_ = siguiente;
}
int getDato() const { return dato_; }
void setDato(int dato) { dato_ = dato; }
Nodo* getSiguiente() const { return siguiente_; }
void setSiguiente(Nodo* siguiente) { siguiente_ = siguiente; }
};
class Lista {
private:
Nodo* cabeza_; // First node of the list
int n_; // Number of nodes
public:
Lista() { // Default constructor: creates an empty list
cabeza_ = nullptr;
n_ = 0;
}
~Lista() { // Destructor: frees every node
while (cabeza_ != nullptr) {
Nodo* borra = cabeza_;
cabeza_ = borra->getSiguiente();
delete borra;
}
}
Lista(const Lista& origin) { // Copy constructor: makes a deep copy
cabeza_ = nullptr;
n_ = 0;
Nodo* aux = origin.cabeza_;
while (aux != nullptr) {
insertarElemento(aux->getDato());
aux = aux->getSiguiente();
}
invertirLista(); // Inserting at the head reverses the order, so reverse it back
}
Lista& operator=(const Lista& origin) { // Copy assignment: frees the old nodes, then copies
if (this != &origin) { // Guard against self-assignment
while (cabeza_ != nullptr) { // Free the nodes the list already has
Nodo* borra = cabeza_;
cabeza_ = borra->getSiguiente();
delete borra;
}
n_ = 0;
Nodo* aux = origin.cabeza_;
while (aux != nullptr) {
insertarElemento(aux->getDato());
aux = aux->getSiguiente();
}
invertirLista();
}
return *this;
}
Lista(Lista&& origin) noexcept { // Move constructor: steals the nodes of the source
cabeza_ = origin.cabeza_;
n_ = origin.n_;
origin.cabeza_ = nullptr; // Leave the source empty
origin.n_ = 0;
}
Lista& operator=(Lista&& origin) noexcept { // Move assignment: frees the old nodes, then steals the new ones
if (this != &origin) { // Guard against self-assignment
while (cabeza_ != nullptr) { // Free the nodes the list already has
Nodo* borra = cabeza_;
cabeza_ = borra->getSiguiente();
delete borra;
}
cabeza_ = origin.cabeza_;
n_ = origin.n_;
origin.cabeza_ = nullptr; // Leave the source empty
origin.n_ = 0;
}
return *this;
}
int getTamano() const { return n_; }
Nodo* getCabeza() const { return cabeza_; }
void insertarElemento(int valor) { // Insert at the head: O(1)
Nodo* nodo = new Nodo(valor, nullptr);
n_ += 1;
nodo->setSiguiente(cabeza_);
cabeza_ = nodo;
}
void imprimirLista() const { // Print every element
Nodo* siguiente = cabeza_;
for (int i = 0; i < n_; i++) {
std::cout << siguiente->getDato() << " ";
siguiente = siguiente->getSiguiente();
}
std::cout << std::endl;
}
void eliminarNodo(int indice) { // Delete the node at the given index
if (indice < 0 || indice >= n_) return; // Invalid index: nothing happens
if (indice == 0) {
Nodo* eliminar = cabeza_;
cabeza_ = eliminar->getSiguiente();
delete eliminar;
n_--;
return;
}
Nodo* eliminanodo_anterio = cabeza_;
for (int i = 0; i < indice - 1; i++) {
eliminanodo_anterio = eliminanodo_anterio->getSiguiente();
}
Nodo* eliminanodo = eliminanodo_anterio->getSiguiente();
eliminanodo_anterio->setSiguiente(eliminanodo->getSiguiente());
delete eliminanodo;
n_--;
}
void invertirLista() { // Reverse the list in place
Nodo* anterior = nullptr;
Nodo* actual = cabeza_;
Nodo* siguiente = nullptr;
for (int i = 0; i < n_; i++) {
siguiente = actual->getSiguiente();
actual->setSiguiente(anterior);
anterior = actual;
actual = siguiente;
}
cabeza_ = anterior;
}
};
int main() {
Lista l1;
l1.insertarElemento(10);
l1.insertarElemento(20);
l1.insertarElemento(30);
Lista l2;
l2.insertarElemento(100);
l2 = l1; // Copy assignment (without swap)
l2.imprimirLista();
Lista l3;
l3 = std::move(l1); // Move assignment (without swap)
l3.imprimirLista();
return 0;
}

This rule of five visualizer shows how a C++ linked list manages its memory. Three lists, l1, l2 and l3, sit side by side so you can build them, insert and remove nodes, reverse them and destroy them. Then try the five special members: the destructor, the copy constructor, copy assignment, the move constructor and move assignment. Each operation runs step by step with the matching line of C++ highlighted, simulated addresses for every node and the detached node drawn while the pointers are rewired. You can see that a copy creates new nodes, that a move steals the old ones and leaves the source empty, and why self-assignment is guarded. Everything runs in your browser.

How to use it

  1. Pick an example from the list or start with the first one, which builds a list and destroys it.
  2. Add operations to any of the three lists with the buttons; constructors only work on lists that are not built yet.
  3. Choose the source list next to each row for copy and move operations, and type a value or an index when needed.
  4. Press Play or move step by step, and follow the highlighted line of C++ and the addresses of the nodes.

Frequently asked questions

What is the rule of five in C++?
If a class manages a resource, it usually needs five special members: destructor, copy constructor, copy assignment, move constructor and move assignment. This tool shows each one acting on a linked list.
What is the difference between copying and moving a list?
A copy creates new nodes with new addresses and leaves the source untouched. A move takes the source nodes, keeps their addresses and leaves the source empty.
What happens with l1 = l1?
The check this != &origin detects the self-assignment and the operator does nothing, so the list is not freed by mistake. The tool shows that case in its own example.
Is the C++ code really compiled?
No. The page simulates each operation in your browser and highlights the matching line of the code, so nothing is sent to any server.

Related tools