Правило пяти

Пошагово разберите деструктор, копирование и перемещение связного списка на C++.

Всё моделируется в этом браузере; ничего не отправляется на сервер.

Операции

Вызывается конструктор

Вызывается конструктор

l1Не создан
∅
  • Текущий
  • Указатель

Шаг 1 / 26

l1
l2
l3

Код

#include <iostream>
#include <utility>
class Nodo { // Узел хранит данные и указатель на следующий
private: // Приватные атрибуты: к ним обращаются только методы
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_; // Первый узел списка
int n_; // Количество узлов
public:
Lista() { // Конструктор по умолчанию: создаёт пустой список
cabeza_ = nullptr;
n_ = 0;
}
~Lista() { // Деструктор: освобождает все узлы
while (cabeza_ != nullptr) {
Nodo* borra = cabeza_;
cabeza_ = borra->getSiguiente();
delete borra;
}
}
Lista(const Lista& origin) { // Конструктор копирования: делает глубокую копию
cabeza_ = nullptr;
n_ = 0;
Nodo* aux = origin.cabeza_;
while (aux != nullptr) {
insertarElemento(aux->getDato());
aux = aux->getSiguiente();
}
invertirLista(); // Вставка в начало переворачивает порядок, поэтому его переворачивают обратно
}
Lista& operator=(const Lista& origin) { // Присваивание копированием: освобождает старые узлы, затем копирует
if (this != &origin) { // Защита от самоприсваивания
while (cabeza_ != nullptr) { // Освобождает узлы, которые уже есть в списке
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 { // Конструктор перемещения: забирает узлы источника
cabeza_ = origin.cabeza_;
n_ = origin.n_;
origin.cabeza_ = nullptr; // Оставляет источник пустым
origin.n_ = 0;
}
Lista& operator=(Lista&& origin) noexcept { // Присваивание перемещением: освобождает старые узлы, затем забирает новые
if (this != &origin) { // Защита от самоприсваивания
while (cabeza_ != nullptr) { // Освобождает узлы, которые уже есть в списке
Nodo* borra = cabeza_;
cabeza_ = borra->getSiguiente();
delete borra;
}
cabeza_ = origin.cabeza_;
n_ = origin.n_;
origin.cabeza_ = nullptr; // Оставляет источник пустым
origin.n_ = 0;
}
return *this;
}
int getTamano() const { return n_; }
Nodo* getCabeza() const { return cabeza_; }
void insertarElemento(int valor) { // Вставка в начало: O(1)
Nodo* nodo = new Nodo(valor, nullptr);
n_ += 1;
nodo->setSiguiente(cabeza_);
cabeza_ = nodo;
}
void imprimirLista() const { // Выводит все элементы
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) { // Удаляет узел по указанному индексу
if (indice < 0 || indice >= n_) return; // Недопустимый индекс: ничего не происходит
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() { // Переворачивает список на месте
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; // Присваивание копированием (без swap)
l2.imprimirLista();
Lista l3;
l3 = std::move(l1); // Присваивание перемещением (без swap)
l3.imprimirLista();
return 0;
}

Этот визуализатор правила пяти показывает, как связный список на C++ управляет памятью. Три списка, l1, l2 и l3, расположены рядом, чтобы вы могли создавать их, вставлять и удалять узлы, переворачивать и уничтожать. Затем попробуйте пять особых функций-членов: деструктор, конструктор копирования, присваивание копированием, конструктор перемещения и присваивание перемещением. Каждая операция воспроизводится пошагово: подсвечивается нужная строка C++, у каждого узла есть имитированный адрес, а отдельный узел рисуется, пока указатели перенастраиваются. Вы увидите, что копия создаёт новые узлы, что перемещение забирает старые и оставляет источник пустым, и почему самоприсваивание защищено. Всё имитируется в вашем браузере.

Как пользоваться

  1. Выберите пример из списка или начните с первого: он создаёт список и уничтожает его.
  2. Добавляйте операции к любому из трёх списков кнопками; конструкторы работают только для ещё не созданных списков.
  3. Выберите список-источник рядом с каждой строкой для копирования и перемещения и введите значение или индекс, когда нужно.
  4. Нажмите «Воспроизвести» или идите по шагам и следите за подсвеченной строкой C++ и адресами узлов.

Часто задаваемые вопросы

Что такое правило пяти в C++?
Если класс управляет ресурсом, ему обычно нужны пять особых функций-членов: деструктор, конструктор копирования, присваивание копированием, конструктор перемещения и присваивание перемещением. Этот инструмент показывает каждую из них на связном списке.
Чем отличается копирование списка от перемещения?
Копия создаёт новые узлы с новыми адресами и не меняет источник. Перемещение забирает узлы источника, сохраняет их адреса и оставляет источник пустым.
Что происходит при l1 = l1?
Проверка this != &origin обнаруживает самоприсваивание, и оператор ничего не делает, поэтому список не освобождается по ошибке. Инструмент показывает этот случай в отдельном примере.
Код на C++ действительно компилируется?
Нет. Страница имитирует каждую операцию в вашем браузере и подсвечивает соответствующую строку кода, поэтому ничего не отправляется на сервер.

Похожие инструменты