Menu
Coddy logo textTech

ネストしたクラスと内部クラス

CoddyのC++ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 38/104。

ネストされたクラスとは、別のクラスの中で定義されたクラスです。この手法は、関連する型をグループ化することでコードを整理するのに役立ち、実装の詳細を隠すことでカプセル化を強化できます。

class LinkedList {
public:
    class Node {    // ネストされたクラス
        int data;
        Node* next;
    public:
        Node(int d) : data(d), next(nullptr) {}
        friend class LinkedList;
    };
    
private:
    Node* head;
    
public:
    LinkedList() : head(nullptr) {}
};

ネストされたクラス NodeLinkedList と密接に結合しています。つまり、そのコンテキスト内でのみ意味を持ちます。ネストさせることで、この関係性を示し、"Node" のような汎用的な名前でグローバルな名前空間を汚染することを避けることができます。

ネストされたクラスは、これまでに学んだものと同じアクセス規則に従います。privateセクションで宣言されたネストされたクラスは、その外部クラス内からのみアクセス可能です。

publicとして宣言されると、外部コードはスコープ演算子を使用してそれを使用できるようになります。

LinkedList::Node node(42);    // Nodeがpublicである場合のみ

方向性に関する重要な詳細が1つあります。入れ子になったクラスは、それを取り囲むクラスのメンバであり、他のメンバと同じアクセス権を持っています。そのため、friend宣言なしで、取り囲むクラスの非公開(private)メンバにアクセスできます。しかし、その逆は自動的ではありません。取り囲むクラスは、入れ子になったクラスの非公開(private)メンバへのアクセス権を持ちません。

そのため、上記の例で Nodefriend class LinkedList; を宣言しているのは、その friend 宣言によって、囲んでいる LinkedListNode のプライベートメンバにアクセスできるようになるからです。これは自動的には行われない一方向のアクセスです。

challenge icon

チャレンジ

簡単

タスクがプロジェクト内で整理される、シンプルなタスク管理システムを構築しましょう。これは入れ子になったクラス(ネストされたクラス)に最適なシナリオです。TaskProjectのコンテキスト内でのみ意味を持つため、内部にネストさせることでコードを整理し、グローバル名前空間の汚染を避けることができます。

コードを整理するために2つのファイルを作成します:

  • Project.h: 入れ子になった Task クラスを含む Project クラスを定義します。

    ネストされた Task クラスは、外部からアクセスできるように Projectpublic セクションで宣言する必要があります。タスク名(string)と完了ステータス(bool)を保持するようにしてください。名前を受け取り、デフォルトでタスクを未完了に設定するコンストラクタを作成します。完了としてマークする complete() メソッド、ステータスを確認する isComplete() メソッド、および名前を取得する getName() メソッドを含めてください。

    Project クラス自体は、プロジェクト名(string)と Task ポインタのコレクションを保持する必要があります。プロジェクト名を受け取るコンストラクタ、新しいタスクを作成してプロジェクトに追加する addTask(const std::string& taskName) メソッド、名前でタスクを検索して完了にする completeTask(const std::string& taskName) メソッド、およびプロジェクト名に続いて各タスクのステータスを表示する displayStatus() メソッドを提供してください。

    TaskProject 内にネストされているため、Project クラスはタスクを直接操作できます。デストラクタで動的に割り当てられたタスクをクリーンアップすることを忘れないでください。

  • main.cpp: 入力からプロジェクト名と2つのタスク名を読み取り(計3行)、すべてをまとめます。指定された名前で Project を作成し、両方のタスクを追加してから、初期ステータスを表示します。最初のタスクを完了させ、再度ステータスを表示して変更を確認します。

    displayStatus() の出力形式は以下の通りです:

    Project: <project_name>
      - <task_name>: [incomplete]
      - <task_name>: [complete]

    各タスクのステータスに基づいて [complete] または [incomplete] を使用してください。

このチャレンジは、ネストされたクラスが関連する型を整理するのにどのように役立つかを示しています。Task クラスは Project と密接に関連しており、単独では意味をなしません。ネストさせることで、コード構造の中でこの関係を明確に示すことができます。

自分で試してみよう

#include <iostream>
#include <string>
#include "Project.h"

using namespace std;

int main() {
    // 入力を読み込む
    string projectName;
    string task1Name;
    string task2Name;
    
    getline(cin, projectName);
    getline(cin, task1Name);
    getline(cin, task2Name);
    
    // TODO: 指定された名前でプロジェクトを作成する
    
    // TODO: 両方のタスクをプロジェクトに追加する
    
    // TODO: 初期状態を表示する
    
    // TODO: 最初のタスクを完了する
    
    // TODO: 変更を表示するために再度ステータスを表示する
    
    return 0;
}
quiz icon腕試し

このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。

オブジェクト指向プログラミングのすべてのレッスン