をるふちゃんのブログ

C++ | DirectX | VC++

CからのC++入門 【クラスを知る】

クラスを使うメリット

構造体をC言語で学習したと思いますが、それを操作する関数を考えてみましょう。

void setStructData( struct Hoge* hoge);

のように構造体のポインタを受け取って、間接参照でその構造体を操作することができます。

しかしよく考えれば、特定の構造体を操作する関数は他の構造体には必要ないわけで、そういうヘルパ関数はひとまとめにしておきたいところです。
また、そのような関数に限って名前が被りやすいという難点もあります。

そのため、「構造体に関数を紐付けられたら便利ではないか?」とは思いませんか。
そこでクラスです。

とりあえず触ってみる

以下のようなC言語の構造体を考えます。

typedef struct Coordinate_ {
  double x;
  double y;
} Coordinate;

xとy座標を保持する簡単な構造体ですが、原点からの距離を求める関数は以下のように書けます。

#include <math.h>

double Distance( struct Coordinate ){
  double t = Coordinate.x * Coordinate.x + Coordinate.y * Coordinate.y;
  return sqrt( t );
}

しかしこの関数はCoordinate構造体以外には関係ありません。
必要ない関数を提供するのはプログラム設計上望ましくありませんし、そもそもその関数名がかぶりそうでよろしくありません。

C++に移植

#include <cmath>

struct Coordinate {
  double x;
  double y;
  
  double Distance();
};

double Coordinate::Distance(){
  double t = this->x * this->x + this->y * this->y;
  return std::sqrt( t );
}

構造体のメンバに関数の前方宣言が追加されていることがわかると思います。
また、このように構造体に紐付けられた関数の記述をする場合、関数名の前にCoordinate::と追加する必要があります。

またtypedefが削除されています。
Cではtypedefしない場合struct Hoge hoge;と書かないといけないのですが、C++ではtypedefしなくてもクラス名を直接Hoge hoge;のように使えます。

メンバ関数の中でメンバ変数にアクセスするのにthis->xとしていますが、これは冗長な表現で、単にxと書くことができます。
ただしメンバ関数の中で同じ名前のローカル変数や引数を使う場合、識別のためにthisを付ける必要があるのであえてこのように記述することもできます。今後のサンプルでは名前はかぶらないようにした上で省略して記述します。

またC言語で使われるhogehoge.hというヘッダはC++においてchogehogeという名前で定義されています。
この場合#inlcude <math.h>#include <cmath>です。


補足: 名前空間

C++では標準ライブラリにはstd::と付ける必要があることにお気づきだと思いますが、これは名前空間という機能です。この名前空間の内側で定義されたクラスや関数には名前空間名::を付加する必要があります。

namespace HogeNS{
//ここに記述したクラスや関数にはHogeNS::とつける必要がある
}

C++の標準ライブラリはすべてstd名前空間の中に記述されている、ということです。

面倒だと思いますか?
確かにそうかもしれません。ですがこの名前空間を階層化することでクラス名の被りを防ぐことができますので今後扱っていきます。


さて、この構造体を使う場合以下のようにします。

#include <iostream>

int main(){
  Coordinate p;//点P
  p.x = 3.f;
  p.y = 4.f;
  std::cout << p.Distance() << std::endl;
  
  return 0;
}

std::cout << p.Distance() << std::endl;C言語printf("%f\n", p.Distance());と同義です。使い方は…わかるかと。iostreamをincludeする必要があります。
ともかくp.Distance()と構造体のメンバ変数と同じように関数を呼べるようになっていることがわかります。
サンプルコードはこちらです。

アクセス指定子

C++の構造体にはアクセスを制限する機能が追加されました。
使い方を見てみましょう。

#include <cmath>

struct Coordinate {
  void set( double x, double y );
  double GetX(){ return x; }
  double GetY(){ return y; }
  double Distance();
private:
  double x;
  double y;
};

void Coordinate::Set( double tX, double tY ){
  x = tX;
  y = tY;
}

double Coordinate::Distance(){
  double t = x * x + y * y;
  return std::sqrt( t );
}

xy座標を格納するメンバ変数への直接のアクセスを防ぐためにprivate:というアクセス指定をしています。
このアクセス指定をするとクラスの関数内部でしかアクセスできなくなります。

例えばこの構造体の実体(インスタンス)があるmain関数などではxy座標に直接アクセスすることができなくなります。このサンプルでは問題ありませんが、この座標が第一象限に限定される実装を行うことになった場合、set/get関数を通じてメンバ変数を操作するならば値チェックが可能です。
しかしメンバ変数のアクセス指定がpublic:である場合、不正な値に書き換えられてしまう可能性が残ります。安全なプログラミングのためにできるだけ活用するするようにしてください。

GetX()GetY()関数ですが、このコードのようにクラスの宣言の中に直接書くことができます。
この場合、関数はinlineに展開されるはずです(されないこともあります)。
もっとも、コンパイラの最適化が働くので分けて書いてもInline展開される場合も多々あるので、単にコーディングの好みの問題になります。個人的にはワンラインで書ける程度のGetter/Setterはヘッダに直接書くことが多いです。

サンプルコードはこちらです。

ところでクラスとは

いままで全くクラスの話が出てこない?
実はCの構造体とC++とのクラスとの差はたったひとつだけです。

クラスではデフォルトのアクセス指定はprivate:であることに対し、
構造体ではpublic:となっています。
これによりCのコードを問題なくコンパイルしつつ安全にコーディングできるようになっています。

今回はここまで

次回はコンストラクタ・デストラクタを扱います。

ちなみにこの記事はこちらに投稿されたものと同じです。