C++のlistクラスを完全ガイド!10のサンプルコードで徹底解説

C++のlistクラスを徹底解説するイメージC++
この記事は約17分で読めます。

 

【サイト内のコードはご自由に個人利用・商用利用いただけます】

この記事では、プログラムの基礎知識を前提に話を進めています。

説明のためのコードや、サンプルコードもありますので、もちろん初心者でも理解できるように表現してあります。

基本的な知識があればカスタムコードを使って機能追加、目的を達成できるように作ってあります。

※この記事は、一般的にプロフェッショナルの指標とされる『実務経験10,000時間以上』を凌駕する現役のプログラマチームによって監修されています。

サイト内のコードを共有する場合は、参照元として引用して下さいますと幸いです

※Japanシーモアは、常に解説内容のわかりやすさや記事の品質に注力しております。不具合、分かりにくい説明や不適切な表現、動かないコードなど気になることがございましたら、記事の品質向上の為にお問い合わせフォームにてご共有いただけますと幸いです。
(送信された情報は、プライバシーポリシーのもと、厳正に取扱い、処分させていただきます。)

はじめに

この記事では、C++で非常に重要なデータ構造の一つであるlistクラスについて詳しく解説します。

初心者から上級者まで、誰もが理解しやすいように、基本から応用まで段階を追って説明します。

C++のlistクラスを理解することで、より効率的で柔軟なコードを書くことができるようになります。

この記事を読めば、listクラスの使い方が明確になり、あなたのC++スキルが新たな段階に進むことでしょう。

●C++とlistクラスの基本

C++は、システムプログラミングやゲーム開発、組込みシステムなど、幅広い用途で使用される汎用プログラミング言語です。

強力な型システム、多機能な標準ライブラリ、そして直接的なハードウェア制御の可能性を提供します。

C++の中核的な特徴の一つは、データ構造の豊富さです。

特に、STL(Standard Template Library)は、さまざまなデータ構造やアルゴリズムを提供し、開発者の生産性を高める重要な役割を果たします。

listクラスは、STLの一部であり、動的な配列やベクターとは異なり、二重連結リストとして実装されています。

この特性により、要素の追加や削除が高速に行え、また、任意の位置へのアクセスには適していませんが、リスト内を順に走査する操作には最適です。

listクラスは、様々なタイプの要素を格納する柔軟性を持ち、プログラムの要件に合わせてカスタマイズすることが可能です。

○C++の基本概念

C++を理解する上で、いくつかの基本的な概念が重要です。

まず、C++はオブジェクト指向プログラミング言語であり、クラスとオブジェクトを使ってデータとその操作をカプセル化します。

また、C++は強い型付けを持ち、変数の型がコンパイル時に決定されます。

これにより、より安全で読みやすいコードの作成が可能になります。

さらに、C++は、ラムダ式、テンプレート、STLなど、強力な機能を多数提供しており、これらを使いこなすことで、効率的で柔軟なプログラムを書くことができます。

○listクラスの概要と特徴

listクラスは、STLの中でも特に汎用性が高いデータ構造の一つです。

listクラスは、要素への追加や削除が容易であり、内部的には二重連結リストとして実装されています。

これにより、ベクターや配列と比較して、中間位置への要素の挿入や削除が高速に行える一方で、ランダムアクセス(特定のインデックスでの要素アクセス)は遅くなります。

また、listクラスは、イテレータを使ってリスト内の要素にアクセスします。

イテレータは、リスト内を走査するための強力なツールであり、要素へのアクセス、変更、削除を容易に行うことができます。

●listクラスの基本的な使い方

C++のlistクラスを使いこなすためには、まず基本的な使い方を理解することが重要です。

listクラスは、要素の追加、削除、そしてイテレーション(要素を順にたどること)を行うための様々なメソッドを提供しています。

ここでは、listクラスの基本的な使い方として、リストの作成と初期化、要素の追加と削除、そしてリストのイテレーションについて説明します。

○サンプルコード1:リストの作成と初期化

まずは、C++でlistクラスのインスタンスを作成し、いくつかの要素で初期化する方法を見ていきます。

#include <iostream>
#include <list>

int main() {
    // listの作成と初期化
    std::list<int> numbers = {1, 2, 3, 4, 5};

    // listの内容を表示
    for (int num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

このコードでは、int型のリストnumbersを作成し、初期値として{1, 2, 3, 4, 5}を与えています。

その後、範囲ベースのforループを使用して、リスト内の各要素を表示しています。

この例では、1 2 3 4 5という順番で数字が出力されます。

○サンプルコード2:要素の追加と削除

listクラスでは、リストの任意の位置に新しい要素を追加したり、既存の要素を削除することができます。

ここでは、要素の追加と削除の基本的な方法を見ていきます。

#include <iostream>
#include <list>

int main() {
    std::list<int> numbers = {1, 2, 3, 4, 5};

    // 要素の追加
    numbers.push_back(6);  // リストの末尾に6を追加
    numbers.push_front(0); // リストの先頭に0を追加

    // 要素の削除
    numbers.pop_back();    // リストの末尾の要素を削除
    numbers.pop_front();   // リストの先頭の要素を削除

    // 変更後のlistの内容を表示
    for (int num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

この例では、push_backメソッドとpush_frontメソッドを使ってリストの末尾と先頭に新しい要素を追加しています。

同様に、pop_backメソッドとpop_frontメソッドを使って末尾と先頭の要素を削除しています。

結果として、リストの内容は2 3 4となります。

○サンプルコード3:リストのイテレーション

リストの各要素にアクセスするためには、イテレーションを行う必要があります。

C++のlistクラスでは、イテレータを使用してリストを走査することができます。

#include <iostream>
#include <list>

int main() {
    std::list<int> numbers = {1, 2, 3, 4, 5};

    // イテレータを使用したリストの走査
    for (std::list<int>::iterator it = numbers.begin(); it != numbers.end(); ++it) {
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    return 0;
}

このコードでは、beginメソッドとendメソッドを使ってリストのイテレータを取得し、リストの全要素を順にアクセスしています。

イテレータは、リストの要素への参照を提供するため、*itのようにして要素の値を取得できます。

●listクラスの詳細な操作方法

C++におけるlistクラスのさらに詳細な操作方法を探求することで、このクラスの機能を最大限に活用することができます。

ここでは、特に要素の検索、リストのソート、そしてリストの逆転という3つの高度な操作に焦点を当てて説明します。

○サンプルコード4:要素の検索

listクラスでは、特定の要素を検索するために線形検索を行う必要があります。

下記のサンプルコードは、list内で特定の値を持つ要素を検索する方法を表しています。

#include <iostream>
#include <list>
#include <algorithm>

int main() {
    std::list<int> numbers = {1, 2, 3, 4, 5};

    // 要素の検索
    auto result = std::find(numbers.begin(), numbers.end(), 3);

    if (result != numbers.end()) {
        std::cout << "要素が見つかりました: " << *result << std::endl;
    } else {
        std::cout << "要素が見つかりませんでした" << std::endl;
    }

    return 0;
}

このコードでは、std::find関数を用いてリスト内で値3を持つ要素を検索しています。

検索結果がリストの終端(end())ではない場合、要素が見つかったことを意味します。

○サンプルコード5:リストのソート

listクラスは、sortメソッドを使用してリスト内の要素をソートすることができます。

下記のサンプルコードは、リストを昇順にソートする方法を表しています。

#include <iostream>
#include <list>

int main() {
    std::list<int> numbers = {5, 3, 1, 4, 2};

    // リストのソート
    numbers.sort();

    // ソートされたリストの表示
    for (int num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

このコードでは、sortメソッドを使用してリストnumbers内の要素を昇順にソートしています。

その結果、リストは1 2 3 4 5の順に並び替えられます。

○サンプルコード6:リストの逆転

listクラスのreverseメソッドを使用すると、リスト内の要素の順序を逆にすることができます。

下記のサンプルコードは、リストの逆転の方法を表しています。

#include <iostream>
#include <list>

int main() {
    std::list<int> numbers = {1, 2, 3, 4, 5};

    // リストの逆転
    numbers.reverse();

    // 逆転されたリストの表示
    for (int num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

このコードでは、reverseメソッドを使用してリストnumbersの要素の順序を逆にしています。

その結果、リストは5 4 3 2 1の順に並び替えられます。

●listクラスの応用例

C++のlistクラスは、その柔軟性と機能の豊富さにより、多様な応用例が可能です。

ここでは、カスタム型をリストに格納する方法、リストを使ったデータ構造の作成、およびリストの結合と分割という3つの応用例について詳しく見ていきます。

○サンプルコード7:カスタム型のリスト

C++のlistクラスは、組み込み型だけでなく、ユーザー定義型(カスタム型)のオブジェクトを格納することもできます。

下記のサンプルコードでは、カスタム型のオブジェクトをリストに格納し、それらを操作する方法を表しています。

#include <iostream>
#include <list>
#include <string>

class Person {
public:
    std::string name;
    int age;

    Person(std::string name, int age) : name(name), age(age) {}
};

int main() {
    // カスタム型のリストを作成
    std::list<Person> people;
    people.push_back(Person("Alice", 30));
    people.push_back(Person("Bob", 25));

    // リスト内の各要素を表示
    for (const auto& person : people) {
        std::cout << person.name << " is " << person.age << " years old." << std::endl;
    }

    return 0;
}

このコードでは、Personクラスのインスタンスをリストに追加しています。

Personオブジェクトは名前と年齢を保持しており、リスト内の各要素がどのようにアクセスされ操作されるかを表しています。

○サンプルコード8:リストを使ったデータ構造

listクラスは、複雑なデータ構造を構築する際にも役立ちます。

例えば、リストを使用して簡単な階層構造を作成することができます。

#include <iostream>
#include <list>
#include <string>

class TreeNode {
public:
    std::string data;
    std::list<TreeNode> children;

    TreeNode(std::string data) : data(data) {}
};

int main() {
    // ツリー構造の作成
    TreeNode root("root");
    root.children.push_back(TreeNode("child1"));
    root.children.push_back(TreeNode("child2"));
    root.children.push_back(TreeNode("child3"));

    // ツリー構造の表示
    std::cout << root.data << std::endl;
    for (const auto& child : root.children) {
        std::cout << " - " << child.data << std::endl;
    }

    return 0;
}

このコードでは、TreeNodeクラスを定義し、それを使用して単純なツリー構造を作成しています。

各ノードはデータと子ノードのリストを保持しています。

○サンプルコード9:リストの結合と分割

listクラスでは、複数のリストを結合したり、特定の条件に基づいてリストを分割することも可能です。

下記のサンプルコードでは、リストの結合と分割の方法を表しています。

#include <iostream>
#include <list>

int main() {
    std::list<int> list1 = {1, 2, 3};
    std::list<int> list2 = {4, 5, 6};

    // リストの結合
    list1.splice(list1.end(), list2);

    // 結合されたリストの表示
    for (int num : list1) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    // リストの分割
    std::list<int> list3;
    auto it = list1.begin();
    std::advance(it, 2);  // イテレータを3番目の要素まで進める
    list3.splice(list3.begin(), list1, it, list1.end());

    // 分割されたリストの表示
    for (int num : list3) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

このコードでは、spliceメソッドを使用して二つのリストを結合し、その後、特定の位置でリストを分割しています。

これにより、リストの内容を柔軟に操作することができます。

●listクラスの注意点と対処法

C++のlistクラスを効果的に使用するためには、いくつかの重要な注意点を理解し、適切な対処法を知っておく必要があります。

特に、パフォーマンスとメモリ管理に関する考慮事項は、listクラスを使用する上で不可欠です。

○パフォーマンスに関する考慮事項

listクラスは、要素の追加と削除に優れていますが、ランダムアクセスのパフォーマンスはvectorクラスなどに比べて劣ります。

listクラスを使用する際は、下記の点に注意してください。

  1. ランダムアクセスが頻繁に必要な場合は、listよりもvectorやarrayを検討する。
  2. 要素の追加や削除が多い場合、またはリストの順序変更が頻繁に行われる場合にlistクラスを選択する。
  3. リストを走査する際には、イテレータを効果的に使用する。

○メモリ管理のベストプラクティス

listクラスは動的メモリを使用するため、メモリ管理にも注意が必要です。

効率的なメモリ管理のためのベストプラクティスは下記の通りです。

  1. 不要になった要素は適時削除することで、メモリの無駄遣いを防ぐ。
  2. 大量のデータを扱う場合は、リストの容量とメモリ使用量を意識する。
  3. カスタム型をリストに格納する場合は、デストラクタやコピーコンストラクタを適切に実装する。

●listクラスのカスタマイズ方法

C++のlistクラスは高い柔軟性を持ち、多様なニーズに応じたカスタマイズが可能です。

ここでは、カスタム比較関数の使用方法とリスト操作のカスタマイズ技法に焦点を当てて解説します。

○サンプルコード10:カスタム比較関数の使用

listクラスのsortメソッドを使用する際、カスタム比較関数を定義して、独自の基準で要素をソートすることができます。

下記のサンプルコードは、カスタム比較関数を使用してリストをソートする方法を表しています。

#include <iostream>
#include <list>
#include <string>
#include <functional>

class Person {
public:
    std::string name;
    int age;

    Person(std::string name, int age) : name(name), age(age) {}

    // カスタム比較関数
    static bool compareAge(const Person& a, const Person& b) {
        return a.age < b.age;
    }
};

int main() {
    std::list<Person> people;
    people.push_back(Person("Alice", 30));
    people.push_back(Person("Bob", 25));
    people.push_back(Person("Charlie", 35));

    // カスタム比較関数を用いてソート
    people.sort(Person::compareAge);

    // ソート結果の表示
    for (const auto& person : people) {
        std::cout << person.name << " is " << person.age << " years old." << std::endl;
    }

    return 0;
}

このコードでは、Personクラス内に静的メソッドcompareAgeを定義し、年齢に基づいてリストの要素をソートしています。

○リスト操作のカスタマイズ技法

listクラスの機能を最大限に活用するためには、イテレータやアルゴリズムとの組み合わせが重要です。

リストをカスタマイズする際には下記の点に注意してください。

  1. イテレータを使用してリスト内の特定の位置を操作する。
  2. 標準ライブラリのアルゴリズム(例えばstd::findstd::remove)を活用して、効率的なリスト処理を実現する。
  3. 必要に応じてリスト内の要素をカスタムクラスで拡張し、独自のメソッドやプロパティを追加する。

これらのカスタマイズ方法を利用することで、C++のlistクラスをさらに柔軟に使用することができます。

まとめ

この記事では、C++のlistクラスの基本から応用、さらにはカスタマイズ方法までを網羅的に解説しました。

listクラスの柔軟性と多機能性を理解し、効率的なプログラミングに活用することができます。

パフォーマンスとメモリ管理に注意を払いながら、listクラスを使いこなすことで、C++プログラミングの幅を広げることができるでしょう。