254 views
 owned this note
## CollegeUnion Wiki [topに戻る](https://hackmd.io/s/rkFqu9xKg) [C++ topに戻る](https://hackmd.io/s/SJ0ni5eFg) # C++入門 01 [クラスについて] ## クラスを使うメリット 構造体をC言語で学習したと思いますが、それを操作する関数を考えてみましょう。 ```cpp void setStructData( Hoge* hoge, int value); ``` のように構造体のポインタを受け取って、間接参照でその構造体を操作することができます。 しかしよく考えれば、特定の構造体を操作する関数は他の構造体には必要ないわけで、そういうヘルパ関数はひとまとめにしておきたいところです。 また、そのような関数に限って名前が被りやすいという難点もあります。 C言語の場合はこれに対処するために ```c typedef struct Hoge_ Hoge ; typedef void ( *HogeSetStructDataFunctype ) ( Hoge*, int value ) ; struct Hoge_ { int foo_ ; HogeSetStructDataFunctype setStructData ; } ; Hoge* newHoge() ; ``` ```c #include "hoge.h" #include <stdlib.h> static void setStructData( Hoge* hoge, int value ){ hoge->foo_ = value ; } Hoge* newHoge() { Hoge* re ; re = calloc( 1, sizeof( Hoge ) ) ; if (NULL == re) { return NULL; } re->setStructData = setStructData ; return re; } ``` のようにして構造体に関数を紐付ける、ということを実現しました。 しかし、関数ポインタは構文が極めて難解です。また関数ポインタは最適化がしづらいという問題もあります。 もっと簡単に構造体に関数を紐付けたい・・・ そこでC++のクラスです。 ## とりあえず触ってみる 以下のようなC言語の構造体を考えます。 ```cpp typedef struct Coordinate_ { double x; double y; } Coordinate; ``` xとy座標を保持する簡単な構造体ですが、原点からの距離を求める関数は以下のように書けます。 ```cpp #include <math.h> double Distance( struct Coordinate c ){ double t = c.x * c.x + c.y * c.y; return sqrt( t ); } ``` しかしこの関数はCoordinate構造体以外には関係ありません。 オブジェクト指向の観点から見ると、必要ない関数を提供するのはプログラム設計上望ましくありません。 ## C++に移植 ```cpp #include <cmath> struct Coordinate { double x; double y; double Distance(); }; inline 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::`と付ける必要があることにお気づきだと思いますが、これは名前空間という機能です。この名前空間の内側で定義されたクラスや関数には`名前空間名::`を付加する必要があります。 ```cpp namespace HogeNS{ //ここに記述したクラスや関数にはHogeNS::とつける必要がある } ``` C++の標準ライブラリはC言語の標準ライブラリにある関数等も含め(マクロは除く)すべて`std`名前空間の中に記述されている、ということです。 面倒だと思いますか? 確かにそうかもしれません。ですがこの名前空間を階層化することでクラス名の被りを防ぐことができますので今後扱っていきます。 *** さて、この構造体を使う場合以下のようにします。 ```cpp #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()`と構造体のメンバ変数と同じように関数を呼べるようになっていることがわかります。 サンプルコードは[こちら](https://gist.github.com/wolf-cpp/3fb03f8460501230f7e6a792b6d81def)です。 ## アクセス指定子 C++の構造体にはアクセスを制限する機能が追加されました。 使い方を見てみましょう。 ```cpp #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; }; inline void Coordinate::Set( double tX, double tY ){ x = tX; y = tY; } inline 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はヘッダに直接書くことが多いです。 サンプルコードは[こちら](https://gist.github.com/wolf-cpp/3d438be26cb1ad458752d4708dea7cbe)です。 ## structとclassの違い ここまでずっと`struct`と書いてきましたが、クラスという呼び名の通り`class`と書くこともできます。 `class`ではデフォルトのアクセス指定は`private:`であることに対し、 `struct`では`public:`となっています。 つまりC++にはクラスしかなく構造体は存在しないということです。 `struct`というキーワードはCにもあり、C++のクラスはCの構造体の上位互換です。 これによりCのコードを問題なくコンパイルしつつ安全にコーディングできるようになっています。 `class`と`struct`のどっちのキーワードを使うかですが、純粋なデータのみでメンバ関数を持たない場合やC++中級者以上の者がテンプレートメタプログラミングなる黒魔術をするときは`struct`を、それ以外は`class`を使う傾向にあるように思えます<sup>[要出典]</sup> ## フリー関数は悪か? 冒頭で ```cpp #include <math.h> double Distance( struct Coordinate c ){ double t = c.x * c.x + c.y * c.y; return sqrt( t ); } ``` のような関数はオブジェクト指向の観点から見ると良くない、という話をしました。クラスに属していない関数を、メンバ関数と対比して、フリー関数と呼びますが、フリー関数は常に悪でしょうか? ```cpp #include <cmath> class Vector2 { public: double x; double y; double Distance() { return std::sqrt(c.x * c.x + c.y * c.y); } }; class Vector3 { double x; double y; double z; double Distance() { return std::sqrt(c.x * c.x + c.y * c.y + c.z * c.z); } }; ``` この例を見てください。2つのクラス`Vector2`,`Vector3`は`Distance`というメンバ関数を持っています。実装も似通っていますね。 C++初心者以上の者はしばしばこういう場合に「Template」「コンセプト」などという謎の言葉を話して入門者には理解不能なコードを書き始めます。<sup>[要出典]</sup> C\++においては、必ずしもオブジェクト指向的に正しいことが受け入れられるわけではないようです。(そもそもC++はオブジェクト指向を実現できるか?という議論もあるが割愛) ## 今回はここまで 次回はコンストラクタ・デストラクタを扱います。