ルール・オブ・ファイブ

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++ の連結リストがメモリをどう管理するかを示します。3 つのリスト l1、l2、l3 が並んでいるので、構築し、ノードを挿入・削除し、反転して破棄できます。続いて 5 つの特殊メンバ関数を試しましょう。デストラクタ、コピーコンストラクタ、コピー代入、ムーブコンストラクタ、ムーブ代入です。各操作はステップごとに再生され、対応する C++ の行が強調され、各ノードには模擬アドレスが付き、ポインタのつなぎ替え中は切り離されたノードも描かれます。コピーは新しいノードを作り、ムーブは古いノードを奪って元を空にすること、自己代入が防がれる理由も確認できます。すべてブラウザ内でシミュレートされます。

使い方

  1. 一覧から例を選ぶか、リストを構築して破棄する最初の例から始めます。
  2. ボタンで 3 つのリストのどれかに操作を追加します。コンストラクタはまだ構築されていないリストにだけ使えます。
  3. コピーやムーブでは各行の横でコピー元のリストを選び、必要なら値やインデックスを入力します。
  4. 再生を押すかステップごとに進め、強調された C++ の行とノードのアドレスを追いかけます。

よくある質問

C++ のルール・オブ・ファイブとは何ですか?
クラスがリソースを管理する場合、通常は 5 つの特殊メンバ関数が必要です。デストラクタ、コピーコンストラクタ、コピー代入、ムーブコンストラクタ、ムーブ代入です。このツールは連結リスト上でそれぞれの働きを示します。
リストのコピーとムーブはどう違いますか?
コピーは新しいアドレスを持つ新しいノードを作り、元は変わりません。ムーブは元のノードを受け取り、アドレスを保ったまま元を空にします。
l1 = l1 ではどうなりますか?
this != &origin の判定が自己代入を検出し、演算子は何もしないので、リストが誤って解放されることはありません。ツールには専用の例があります。
C++ コードは実際にコンパイルされますか?
いいえ。ページはブラウザ内で各操作をシミュレートし、対応するコードの行を強調するだけで、サーバーには何も送られません。

関連ツール