書籍転載
文法からはじめるプログラミング言語Microsoft Visual C++入門

C++のクラスをマスターしよう(前編)
―第10章 クラス〜オブジェクト指向プログラミング(前編)―

WINGSプロジェクト 矢吹 太朗(監修 山田 祥寛)
2010/05/19

10.1.3 メソッド

 メソッドはクラスの定義の中で次のように宣言します。構文は関数の場合(5.1節)と同じです。

struct 型名
{
  戻り値の名前 メソッド名(パラメータリスト);
  (複数のメソッドを宣言することも可能)
};
[構文]メソッドの宣言

 メソッドの定義はクラス定義の外で行います。メソッド定義の構文も関数定義の構文とほとんど同じですが、メソッド名の前にクラス名を書いて、そのメソッドが属するクラスを明示します。

戻り値の型 クラス名::メソッド名(パラメータリスト)
{
  文
}
[構文]メソッドの定義

 例として、先に作成したクラスPersonに「名前 (年齢)」を表示するメソッドshow()を実装すると次のようになります。

struct Person
{
  string name;
  int age;
  void show(); //メソッドの宣言
};

//メソッドの定義
void Person::show()
{
  cout<<name<<" ("<<age<<")\n";
}
[サンプル]10-person2.cpp

 メソッドの利用方法はフィールドの場合と同じで、変数がオブジェクトのときにはメンバ演算子「.」を、ポインタのときには矢印演算子「->」を使います。

Person taro;
taro.name="Taro";
taro.age=32;
taro.show(); //出力値:Taro (32)

Person* pHanako=new Person();
pHanako->name="Hanako";
pHanako->age=27;
pHanako->show(); //出力値:Hanako (27)
delete pHanako;
[サンプル]10-person2.cpp

 次のように、クラス定義の中でメソッドを定義することもできます。

struct Person
{
  string name;
  int age;

  //クラス定義の中でメソッドも定義する
  void show()
  {
    cout<<name<<" ("<<age<<")\n";
  }
};
[サンプル]10-person3.cpp

 これ以降では、記述を簡潔にするためにこの方法でメソッドを定義することにします。実はこの記法は、宣言と定義を分けた先ほどの記法と同等ではありません。クラス宣言の中でメソッドを宣言することは、キーワードinlineを付けてメソッドを宣言するのと同じ意味になります。キーワードinlineを付けて宣言したメソッドは、コンパイラが妥当と判断すれば関数呼び出しのオーバーヘッドがない形に変換されます。これによって、多くの場合はプログラムが高速になります。

 プログラムが大きくなる場合には、クラスの定義をヘッダファイル(.h)に、メソッドの定義をソースファイル(.cpp)に書くのが一般的です。その際には、メソッドを定義するソースファイルの先頭で、クラスを定義したヘッダファイルを読み込みます(#includeを使います)。本書の大部分ではコードを単純にするために、ソースファイルの中でクラスの定義を記述し、メソッドの定義もクラス定義の中で行っています。

10.1.4 静的メンバ

 キーワードstaticを使ってメンバの宣言や定義を行うと、そのメンバは静的メンバになります。静的メンバは、そのクラスのすべてのオブジェクトで共有されます。また、スコープ演算子::を使うことで、オブジェクトがなくても利用することができます。以下に例を示します。

#include <iostream>
using namespace std;

struct A
{
  static int s; //静的フィールド
  static void showS() { cout<<s<<endl; } //静的メソッド
};

//静的フィールドは、クラス定義の外で初期化する
int A::s=5; //これを省略すると0になる

int main()
{
  //静的はオブジェクトがなくても利用できる
  A::showS(); //出力値:5

  A a1, a2;
  a1.s=10; //a1.sの変更が
  a2.showS(); //a2.sにも反映される
  //出力値:10
}
[サンプル]10-staic.cpp

 このプログラムでは、静的メンバのみを持つクラスAを定義し、その静的フィールドsを初期化しています。静的フィールドの初期化は、クラス定義の外で行うことに注意してください。

10.1.5 アクセス制御

 名前や年齢をオブジェクトにまとめることによって、まとめて操作できるようになりました。この段階ではこれでよいのですが、データをうまく抽象化するためには、nameやageのようなオブジェクトの内部状態を、外部から直接アクセスできないようにする方法を知っておかなければなりません。

 クラスの定義において、private:という記述の後で宣言したメンバには外部からアクセスできなくなります。反対に、public:という記述の後で宣言したメンバには外部からアクセスできます。以下に例を示します。

struct A
{
private:
  int i;
public:
  int j;
};

int main()
{
  A a;
  //a.i=10; //エラー(iはprivate)
  a.j=5; //OK
}
[サンプル]10-access1.cpp

 クラスAのフィールドjはpublicなので、外部(関数main())からアクセスできます。その一方で、a.i=10;というコードをビルドしようとすると、コンパイルエラーが発生します。フィールドiはprivateなので、外部からはアクセスできないのです。

 キーワードstructで定義したクラスのメンバは、既定ではpublicになります。そのため、前述のクラスAの定義は、次のように書くこともできます。

struct A
{
  int j; //structの既定はpublic:
private:
  int i;
};
[サンプル]10-access2.cpp

 クラスを定義するキーワードには、structの他にclassがあり、classを使って定義したクラスのメンバは、既定でprivateになります。classとstructの違いはこれだけです*2。キーワードclassを使うと、前述のクラスAの定義は次のようになります。

class A
{
  int i; //classの既定はprivate:
public:
  int j;
};
[サンプル]10-access3.cpp

*2 誤解を避けるために、public やprivate は必ず記述すべきだという意見もあります。この意見に従うなら、class とstruct を使い分ける必要はありません。本書では、記述がより簡潔になる方を使うようにしています。

 本書では、クラスという語は、オブジェクトのひな形(つまりclassとstructで定義されるもの)を指すために用い、キーワードclassで定義されるものだけを指すためには用いません。キーワードstructで定義されるものだけを特に「構造体」と呼ぶことがありますが、この用語はC言語における構造体(フィールドのみから成り、メソッドを持たないようなオブジェクト、あるいはそのひな形)を指すために用いられることも多いので注意してください。

 キーワードprivateを付けて宣言し、外部からアクセスできないようにしたフィールドを操作したい場合には、そのためのメソッドを用意します。以下に例を示します。

#include <iostream>
using namespace std;

struct A
{
private:
  int i;
public:
  int j;
  void setI(int val) { i=val; } //iの値をセットするためのメソッド
  int getI() { return i; } //iの値を取得するためのメソッド
};

int main()
{
  A a;
  //a.i=10; //エラー(iはprivate)
  a.setI(10); //iはメソッドを使って操作する
  cout<<a.getI()<<endl;//出力値: 10

  a.j=20; //OK(jはpublic)
  cout<<a.j<<endl;//出力値: 20
}
[サンプル]10-access4.cpp

 ここで作成したクラスAのメンバへのアクセス制御のようすを図10-2にまとめました。これはクラスの仕様を図示するためのUMLクラス図です(1.1.4項を参照)。UMLクラス図においては、publicメンバには「+」をprivateメンバには「-」を付けることになっています。10.2.2項で紹介するprotectedメンバには「#」を付けます。クラスAの内部からはすべてのメンバにアクセスすることができますが、関数main()のような、クラスAの外部からはpublicなメンバにしかアクセスすることができません。

図10-2 クラスのメンバへのアクセス制御

 この段階では、メンバの操作が面倒になっただけにしか見えないかもしれませんが、ここで紹介したように内部を隠蔽したい場合の他に、変数の値を設定する前にチェックするようにしたい場合などにもこのしくみは有用です。書き捨ての小さいプログラムを別にして、フィールドはすべてprivateにし、必要な場合にはフィールドを操作するためのメソッドを用意することを基本原則にしておくとよいでしょう*3

*3 アクセス制御はオブジェクト単位ではなくクラス単位で行われます。たとえば、クラスA のオブジェクトa1 とa2 があるときに、a2のメソッドにa1を渡せば、そのメソッドはa1のprivate メンバにアクセスすることができます。


 INDEX
  [書籍転載]文法からはじめるプログラミング言語Microsoft Visual C++入門
  C++のクラスをマスターしよう(前編)
    1.クラスとは/クラスの生成
  2.メソッド/静的メンバ/アクセス制御
    3.コンストラクタ
    4.デストラクタ/デストラクタの役割
    5.リソース確保は初期化である

インデックス・ページヘ 「文法からはじめるプログラミング言語Microsoft Visual C++入門」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間