Version 16.06
インラインメンバ関数
『質問!』
「はい火美ちゃん」
『前回、基本的なクラスについて教えてもらいました』
// Data.h
// CDataクラス。
class CData
{
private:
// private メンバ変数。
int m_iData;
public:
// m_iData の getter 。
int GetData() const;
// m_iData の setter 。
void SetData( int p_iData );
};
// Data.cpp
#include <Windows.h>
#include <stdio.h>
#include "Data.h"
// m_iData の getter 。
int CData::GetData() const
{
return m_iData;
}
// m_iData の setter 。
void CData::SetData( int p_iData )
{
m_iData = p_iData;
}
『でも、 Version 11.16 ( No.216 ) の時は、こうでした』
// メンバ関数を持つクラス。
class CHasMemberFunction
{
private:
int m_iData;
public:
// 値を返します。
int GetData() const
{
return m_iData;
}
// 値をセットします。
void SetData( int p_i )
{
m_iData = p_i;
}
};
『どっちが正しいんやねん!』
「そうだね、この使い分けについて説明していなかったね。まず、基本的な
機能は同じ。単純に言えば、書き方の問題、プログラミングスタイルの違い
です」
『見た目とか、プログラムの癖とかそーゆーこと?』
「基本的にはね。細かい所はちょっと違うので、それをこれから説明しま
す」
『ほい』
「まず、 Version 11.16 ( No.216 ) の CHasMemberFunction クラスのよう
に、クラスの中に入れられたメンバ関数は【インラインメンバ関数】と言い
ます」
『いんらいんめんばかんすう……?』
「そう。このメンバ関数はちょっと特殊で、呼び出し元に関数そのものが埋
め込まれます」
『埋め込まれる……って、マクロみたいに?』
「そういうこと。だから、呼び出しがちょっとだけ早くなります」
『でも、それってファイルサイズ大きくならない?』
「なります。それに、実際には埋め込まれるとは限らないから」
『へ?』
「 Version 15.23 ( No.323 ) で、【関数のインライン展開の制御】ってい
うオプションがあったでしょ」
『あ! そういえばあの時も関数が埋め込まれるとかどうとか』
「これをオンにしないと埋め込まれないから、この〈埋め込まれる〉ってい
う機能はおまけとして考えて、あまりこだわらない方がいいかな」
『つまりそれを目的にして、こんな書き方しない方がいいってことね』
「そういうこと。じゃあ、メンバ関数を分けるのと分けないの、どちらが
いいのかという点について。まず、分けない場合、通常はヘッダーファイル
に書きます」
// Data.h
// CDataクラス。
class CData
{
private:
// private メンバ変数。
int m_iData;
public:
// m_iData の getter 。
int GetData() const
{
return m_iData;
}
// m_iData の setter 。
void SetData( int p_iData )
{
m_iData = p_iData;
}
};
// Data.cpp
// (空)
「このように、ヘッダーファイルの方に全て入れて、ソースファイルの方は
何も書かないようにします」
『??? それおかしいじゃん。コンパイルはソースファイル単位でするん
だから、こんなことしたらコンパイルされない……んじゃなくて、
インクルードしたソースファイル全部でコンパイルされちゃうんじゃ……』
「それがされないんです。確かにソースファイルがないとそのオブジェクト
ファイルは作られないんだけど、インラインメンバ関数最初に使用する
ソースファイルでコンパイルしたら、あとはそれを使い回すんです」
『オブジェクトファイルがなくてもいい……他のオブジェクトファイルに
入ってるってこと?』
「そういうこと。だから、インラインメンバ関数の場合、ヘッダーファイル
に関数の実装を書ける、ということになります」
『ヘッダーファイルに書ける……とどうなるの?』
「ソースファイルが要らなくなります」
『ソースファイルが要らない……ちょっとそれ楽かも』
「でしょ。 C++ 言語は、ヘッダーファイルとソースファイルっていう2つの
ファイルでプログラムを管理するから、複雑になりやすいんです」
『それが、インラインメンバ関数を使えばヘッダーファイルだけになる!』
「 Java とかの、 C++言語以外のプログラミング言語は1ファイルだけで済
むものがほとんどだから、それに比べて C++言語はとても面倒なんだよね」
『うん、面倒面倒! この方がずっと楽! なんだ、じゃあ全部
ヘッダーファイルでやればいいんじゃん』
「実際、そういうふうに書かれたプログラムもあるよ。だから、そういう
流れもあるかも」
『じゃあ』
「でも、僕はお勧めしません」
『ええー?』
「一番の理由は、相互参照の解決が難しい点」
『相互参照……ってどっかで聞いたね』
「 Version 15.05 ( No.305 ) で説明したでしょ。あるヘッダーファイルが
他のヘッダーファイルの機能を使用する時に、順序がうまくいかなくなる、
っていうの」
『あー』
「たとえば CData クラスと CData2 クラスが以下のようになっていた場合」
// Data.h
// CDataクラス。
class CData
{
public:
void Output()
{
OutputDebugString( "CData::Output()\n" );
}
void UseCData2()
{
CData2 cData2;
cData2.Output();
}
};
// Data2.h
// CData2クラス。
class CData2
{
public:
void Output()
{
OutputDebugString( "CData2::Output()\n" );
}
void UseCData()
{
CData cData;
cData.Output();
}
};
『う、両方で使い合ってるね。これじゃ、使う方はどっちを先に
インクルードしてもエラーになるね……』
「これを解決するためには、定義の部分をソースファイルに移すしかないか
ら、結局ソースファイル使った方がいいかな。」
『それ以外にもデメリットってある?』
「結局問題は、定義の中ではいろんな関数やクラスを使う、ってこと。
ヘッダーファイルにそれを書くってことは、そのヘッダーファイルを
インクルードする前に、そういう関数やクラスの宣言が書かれた
ヘッダーファイルをインクルードしなきゃいけないでしょ」
『そか、ヘッダーファイルをインクルードするために、たくさんの
ヘッダーファイルをインクルードしなきゃいけないんだ』
「どれをインクルードしなきゃいけないとか、順番も気にしなきゃいけない
でしょ。ソースファイルに書いちゃえば、そのソースファイルで
インクルードすればいいだけだから」
『確かにその方が簡単そうだね……む、結局そうなっちゃうんだ』
「 C++言語は元々複雑な言語だから、ちょっと手間が掛かっても、
ソースファイルとヘッダーファイルの両方作った方が分かりやすくなるよ」
/*
Preview Next Story!
*/
『うーん、今回のは結構良さそうだったのになー』
「 C++言語は簡単そうなものも実は複雑だから」
『他のも? たとえば?』
「たとえば、ポインタに対する参照とか」
『あー、参照って簡単に使えるけど、逆にその辺があやふやかも』
「というわけで次回」
< Version 16.07 ポインタと参照のおさらい >
『につづく!』
「クラスをちゃんと理解する上で重要なおさらいです」
『おさらいはおさらいだと思うけど……』