Rule of Five

Verfolge Schritt für Schritt Destruktor, Kopieren und Verschieben einer verketteten C++-Liste.

Alles wird in diesem Browser simuliert; nichts wird an einen Server gesendet.

Operationen

Der Konstruktor wird aufgerufen

Der Konstruktor wird aufgerufen

l1Nicht konstruiert
∅
  • Aktuell
  • Zeiger

Schritt 1 / 26

l1
l2
l3

Code

#include <iostream>
#include <utility>
class Nodo { // Ein Knoten enthält die Daten und einen Zeiger auf den nächsten
private: // Private Attribute: nur die Methoden greifen darauf zu
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_; // Erster Knoten der Liste
int n_; // Anzahl der Knoten
public:
Lista() { // Standardkonstruktor: erzeugt eine leere Liste
cabeza_ = nullptr;
n_ = 0;
}
~Lista() { // Destruktor: gibt alle Knoten frei
while (cabeza_ != nullptr) {
Nodo* borra = cabeza_;
cabeza_ = borra->getSiguiente();
delete borra;
}
}
Lista(const Lista& origin) { // Kopierkonstruktor: erstellt eine tiefe Kopie
cabeza_ = nullptr;
n_ = 0;
Nodo* aux = origin.cabeza_;
while (aux != nullptr) {
insertarElemento(aux->getDato());
aux = aux->getSiguiente();
}
invertirLista(); // Das Einfügen am Anfang kehrt die Reihenfolge um, also wird sie zurückgedreht
}
Lista& operator=(const Lista& origin) { // Kopierzuweisung: gibt die alten Knoten frei und kopiert dann
if (this != &origin) { // Schützt vor Selbstzuweisung
while (cabeza_ != nullptr) { // Gibt die Knoten frei, die die Liste bereits hat
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 { // Verschiebekonstruktor: übernimmt die Knoten der Quelle
cabeza_ = origin.cabeza_;
n_ = origin.n_;
origin.cabeza_ = nullptr; // Lässt die Quelle leer zurück
origin.n_ = 0;
}
Lista& operator=(Lista&& origin) noexcept { // Verschiebezuweisung: gibt die alten Knoten frei und übernimmt dann die neuen
if (this != &origin) { // Schützt vor Selbstzuweisung
while (cabeza_ != nullptr) { // Gibt die Knoten frei, die die Liste bereits hat
Nodo* borra = cabeza_;
cabeza_ = borra->getSiguiente();
delete borra;
}
cabeza_ = origin.cabeza_;
n_ = origin.n_;
origin.cabeza_ = nullptr; // Lässt die Quelle leer zurück
origin.n_ = 0;
}
return *this;
}
int getTamano() const { return n_; }
Nodo* getCabeza() const { return cabeza_; }
void insertarElemento(int valor) { // Am Anfang einfügen: O(1)
Nodo* nodo = new Nodo(valor, nullptr);
n_ += 1;
nodo->setSiguiente(cabeza_);
cabeza_ = nodo;
}
void imprimirLista() const { // Gibt alle Elemente aus
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) { // Löscht den Knoten am angegebenen Index
if (indice < 0 || indice >= n_) return; // Ungültiger Index: nichts passiert
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() { // Kehrt die Liste an Ort und Stelle um
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; // Kopierzuweisung (ohne swap)
l2.imprimirLista();
Lista l3;
l3 = std::move(l1); // Verschiebezuweisung (ohne swap)
l3.imprimirLista();
return 0;
}

Dieser Visualisierer der Rule of Five zeigt, wie eine verkettete C++-Liste ihren Speicher verwaltet. Drei Listen, l1, l2 und l3, stehen nebeneinander, damit du sie aufbauen, Knoten einfügen und löschen, sie umkehren und zerstören kannst. Probiere dann die fünf speziellen Memberfunktionen aus: den Destruktor, den Kopierkonstruktor, die Kopierzuweisung, den Verschiebekonstruktor und die Verschiebezuweisung. Jede Operation läuft Schritt für Schritt ab, mit hervorgehobener C++-Zeile, simulierten Adressen für jeden Knoten und dem losgelösten Knoten, während die Zeiger neu verbunden werden. Du siehst, dass eine Kopie neue Knoten erzeugt, dass ein Verschieben die alten übernimmt und die Quelle leer lässt, und warum die Selbstzuweisung abgesichert ist. Alles wird in deinem Browser simuliert.

So funktioniert es

  1. Wähle ein Beispiel aus der Liste oder beginne mit dem ersten, das eine Liste aufbaut und wieder zerstört.
  2. Füge den drei Listen mit den Schaltflächen Operationen hinzu; Konstruktoren funktionieren nur bei Listen, die noch nicht konstruiert sind.
  3. Wähle neben jeder Zeile die Quellliste für Kopier- und Verschiebeoperationen und gib bei Bedarf einen Wert oder Index ein.
  4. Drücke Abspielen oder gehe Schritt für Schritt vor und verfolge die hervorgehobene C++-Zeile und die Adressen der Knoten.

Häufig gestellte Fragen

Was ist die Rule of Five in C++?
Verwaltet eine Klasse eine Ressource, braucht sie meist fünf spezielle Memberfunktionen: Destruktor, Kopierkonstruktor, Kopierzuweisung, Verschiebekonstruktor und Verschiebezuweisung. Dieses Werkzeug zeigt jede davon an einer verketteten Liste.
Was ist der Unterschied zwischen Kopieren und Verschieben einer Liste?
Eine Kopie erzeugt neue Knoten mit neuen Adressen und lässt die Quelle unverändert. Ein Verschieben übernimmt die Knoten der Quelle, behält ihre Adressen und lässt die Quelle leer.
Was passiert bei l1 = l1?
Die Prüfung this != &origin erkennt die Selbstzuweisung und der Operator tut nichts, sodass die Liste nicht versehentlich freigegeben wird. Das Werkzeug zeigt diesen Fall in einem eigenen Beispiel.
Wird der C++-Code wirklich kompiliert?
Nein. Die Seite simuliert jede Operation in deinem Browser und hebt die passende Codezeile hervor; es wird nichts an einen Server gesendet.

Verwandte Tools