ショートカット
ファシリテーター × あり方
コーディングの向こう側
Hello, ANOTHER world!
オブジェクト指向のはなし
プログラミングのはなし
C言語実力診断クイズ
eSkillBooks
オブジェクト指向のはなし

コンポジションの記述

導入

コンポジションは、内蔵したいインスタンスをメンバ変数にすることによって実現されます。大きなオブジェクトを内蔵する場合は、スタック領域の圧迫を避けるためにコンストラクタとデストラクタで new/delete を使います。

用語
このトピックでは次の用語を覚えます:
  • new/delete キーワード
解説
private メンバによるコンポジション

「めがね (EyeGlasses)」クラスを考えてみましょう。めがねはレンズとフレームと小さな蝶番とネジからできています。話を単純にするために、「めがねは2枚のレンズから構成される」ということにしましょう。これをコンポジションで表現します。

まず、「レンズ (Lens)」クラスがあるものとします。これが「めがね」クラスの構成要素となります。「レンズ」クラスは次のように宣言されているものとします(定義も必要ですが、ここでは省略します)。

SAMPLE CODE
Lens クラス宣言ファイル(ファイル名:"Lens.h")

#ifndef INCLUDED_LENS
#define INCLUDED_LENS

class Lens {
public:
    Lens();
    virtual ~Lens();
};

#endif // #ifndef INCLUDED_LENS

「レンズ」クラスから「めがね」クラスを作るには、「レンズ」を「めがね」のメンバ変数にします。つまり、「レンズ」クラスのインスタンスを「めがね」クラス宣言に private メンバとして記述します。「レンズ」は左右2枚必要なので、「めがね」のクラス宣言は次のようになります(定義も必要ですが、ここでは省略します)。

SAMPLE CODE
EyeGlasses クラス宣言ファイル(ファイル名:"EyeGlasses.h")

#ifndef INCLUDED_EYEGLASSES
#define INCLUDED_EYEGLASSES

#include "Lens.h"

class EyeGlasses {
public:
    EyeGlasses();
    virtual ~EyeGlasses();
private:
    Lens m_LeftLens;
    Lens m_RightLens;
};

#endif // #ifndef INCLUDED_EYEGLASSES
スタックの圧迫を避ける

上のように private メンバとしてコンポジションを記述するのは、「レンズ」オブジェクトのサイズが小さいときだけです。オブジェクトのサイズがあまり大きくなるとスタック領域を圧迫してしまうためです。構成要素のオブジェクト(ここでは「レンズ」)が大きいときには、これらをヒープ領域に確保するようにします。

ヒープを使ってコンポジションを行うためには、次のように private メンバとして「レンズ」オブジェクトへのポインタを記述します。

SAMPLE CODE
EyeGlasses クラス宣言ファイル(ファイル名:"EyeGlasses.h")

#ifndef INCLUDED_EYEGLASSES
#define INCLUDED_EYEGLASSES

#include "Lens.h"

class EyeGlasses {
public:
    EyeGlasses();
    virtual ~EyeGlasses();
private:
    Lens *m_pLeftLens;
    Lens *m_pRightLens;
};

#endif // #ifndef INCLUDED_EYEGLASSES

実際にヒープ上でインスタンスを確保するのは new 演算子です。ヒープ上のインスタンスを解放するのは delete 演算子です。「めがね」の構成要素である「レンズ」の確保・解放を行うために、コンストラクタとデストラクタを使います。具体的には、次のようになります。

SAMPLE CODE
EyeGlasses クラス定義ファイル(ファイル名:"EyeGlasses.c")

#include "EyeGlasses.h"

// コンストラクタ
EyeGlasses::EyeGlasses()
{
    m_pLeftLens  = new Lens;
    m_pRightLens = new Lens;
    
    if ( ( m_pLeftLens == 0 ) || ( m_pRightLens == 0 ) ) {
        ; // 確保失敗時処理
    }
}

// デストラクタ
EyeGlasses::~EyeGlasses()
{
    if ( m_pLeftLens ) {
        delete m_pLeftLens;
    }
    
    if ( m_pRightLens ) {
        delete m_pRightLens;
    }
}

コンストラクタでは、「めがね」オブジェクトを使用可能にするために new を使って「レンズ」オブジェクトの確保を行っています。レンズは2枚あるため2度 new が使われています。new はヒープ領域が不足している場合には失敗します。そのため、確保に成功したかどうかを判定し、失敗時にはエラー処理を行う必要があります。

デストラクタでは delete を用いて「レンズ」オブジェクトを解放しています。確保に失敗したときのことを考えて、ポインタが有効なアドレスを指しているときだけ解放するようにしています。

もっと!
もっと詳しく知りたい人は次の用語について調べると良いでしょう:
  • 配列オブジェクトの new[] / delete[]
  • new ハンドラ

(「オブジェクト指向のはなし」は1999年2月から2000年4月にかけて作成されたコンテンツです。)