メンバ変数はprivateにしよう
「メンバ変数は private にする」
これは一般的なルールです。
メンバ変数を隠蔽する、つまりクラスの外から見えないようにします。
メンバ変数は大切なものです。
大切なものは他人に勝手に触られないように隠すのが常識です。
でも、外からメンバ変数にアクセスしたい場合もありますよね?
そのために getter や setter と呼ばれる public な関数を用意します。
コードにすると、こんな感じです。
class Sample {
public:
int getValue() {
return _value;
}
void setValue( int value ) {
_value = value;
}
private:
int _value;
}
getter/setter のことを「アクセサ」と呼びます。
「汗くさ」じゃなくて「アクセサ(accessor)」です。
「アクセスするもの」という意味です。
普通に考えると "アクセス + er" になると思いますが、英語の謎ルールで er ではなく or になります。
こうやってアクセサを使うことで、private にして隠蔽したメンバ変数にアクセスすることができるようになります。
「いや、ちょって待て。」
「それじゃあ、メンバ変数を public にしたのと変わらないじゃん。」
そう思う方もいるでしょう。
隠蔽性に関して言えば、先ほどのクラスは、このクラスと大差ありません。
class Sample {
public:
int value;
}
どちらもメンバ変数の値を取得できます。
int x = sample.getValue();
int x = sample.value;
どちらもメンバ変数に値を設定できます。
sample.setValue( 10 );
sample.value = 10;
それでも、public なメンバ変数にするより、アクセサを使うべきです。
アクセサのメリット1:ReadOnlyにできる
メンバ変数をクラスの外に公開する理由は、値の設定だけではありません。
値を参照したいという理由で公開したい場合もあります。
その場合、public変数方式だと、取得にしか使わないのに、メンバ変数は外から変更可能な状態になってしまいます。
当然、必要ないならメンバ変数は外から変更可能にすべきではありません。
一方、アクセサ方式だと、setter を宣言せずに getter だけ宣言すれば、メンバ変数の値は取得しかできません。
こちらの方法だと外からメンバ変数を変更することはできないので安全です。
逆に setter だけ宣言すれば、WriteOnly な状態も作れます。
アクセサのメリット2:メンテナンス性
「たまに _value が変な値になることがある」という不具合が発生した場合、public変数だと、値を設定している箇所を全て調べなければなりませんが、アクセサだと setter にブレークポイントを貼るだけで犯人が特定できます。
同様に、getter/setter にブレークポイントを貼れば、変数を使うタイミングを検出したり、変数にアクセスしようとしている輩を簡単に判別したりすることができます。
アクセサのメリット3:カスタマイズ性
アクセサ方式だと、値の取得や設定の処理をカスタマイズできるようになります。
例えば、取得時、状況によって返す値を変えることが可能です。
// 無効な場合は 0 を返す
int getValue() {
if ( isEnabled() ) {
return _value;
}
else {
return 0;
}
}
値を設定する際の範囲チェックもできます。
// マイナスなら 0 を設定する
void setValue( int value ) {
if ( value < 0 ) {
_value = 0;
}
else {
_value = value;
}
}
また、「値を設定したときに、ついでにテキストも更新しておいて」と仕様が追加されたらどうなるでしょう?
public変数方式だと、value に値を設定している箇所を検索して、その全ての場所で対応しなければなりません。
しかし、アクセサ方式だと、以下のように setter を修正するだけで済みます。
void setValue( int value ) {
_value = value;
_text = ...(略)...
}
public変数のメリット
アクセサで変数にアクセスするようにすると、オーバーヘッドが気になるところです。
アクセサ経由で変数にアクセスするよりも、変数にダイレクトにアクセスする方が早いのは想像に難しくありません。
ただ、よっぽどパフォーマンスにシビアな環境ではない限り、気にするほどの差は無いと思います。
それに、getter/setter をインライン化すればオーバーヘッドは阻止できると思いますし、インライン化しなくても最適化をかければオーバーヘッドが無くなるかもしれません。
実際に確認したわけではないので断言はできませんが。
(そこまでパフォーマンスにシビアな環境にいたことがないので、調べたことがありません。)
public変数の最大のメリットは、実装の容易さでしょう。
private な変数と getter/setter の3つを用意するより、public な変数1つだけを用意する方が簡単です。
とにかく急いで機能だけ実装したい場合は有効かもしれません。
もちろん、後でリファクタリングしてアクセサ方式に変えるという前提で。
また、テスト用に一時的に実装して、使い終わったら削除するつもりのクラスはpublic変数でも良いでしょう。
逆に言うと、public変数はそれぐらいしかメリットが無いような気がします。
ですので、積極的にアクセサを使いましょう!