質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.10%

アクセサにするべきかpublicにするべきか?

解決済

回答 10

投稿 編集

  • 評価
  • クリップ 2
  • VIEW 11K+

Chironian

C++総合1位

すいません。まず、この話題はC++言語限定とさせて下さい。広げると収拾がつかなくなる予感がしますので。

下記のようなメンバ変数mMemberへのアクセス方法について、皆さんはどちらで定義しますか?
たぶんケースバイケースと思います。どんな時にprivate定義してアクセサを提供(Foo型)し、どんな時にpublic定義(Bar型)しますか?
class Foo
{
private:
    int  mMember;
public:
    int getMember() const {return mMember;}
    void setMember(int iMember) {mMember=iMember;}
        :
};

struct Bar
{
    int  mMember;
        :
};
FooもBarと同様にmMemberを事実上全て開示してます。ですので、Foo型を用いるメリットはmMemberがアクセスされる際に何か処理を追加してもI/Fを変えないで済むことだけです。ですので、処理を追加する可能性がほぼ想定されないなら、無駄に複雑にしないためにBar型を使うべきです。
しかし、構造体(データを保持することが主目的)として設計したクラスであれば良いのですが、オブジェクト指向的に設計したクラスでは抵抗を感じてしまい、躊躇してます。
そこで、参考にさせて頂きたく、皆さんのご意見をお聞かせ下さい。

結果報告

この議論を通じて、やっと自分なりの方針を明確にできました。
皆さん、ありがとうございます。

ベストアンサーは、C++らしい方針を最も明確に記載して頂けたcatsforepawさんにさせて頂きました。
また、「ブレーク貼れるようにアクセサ」という使い方に気づかせて頂けたこともありがたいです。

【検討のポイント】
私はシンプルイズベスト信者なので、メリットのない複雑さは悪と考えます。
なので、メリットもないのにアクセサを設けるのは悪なわけです。
ということは、アクセサを設けるメリットは何か?がポイントになります。

なお、アクセスされたタイミングで何らかのアクションを行うようなケースはメリットもなにも必須事項なので検討不要ですね。
また、そのような機能の追加も含め、何らかの変更が予想されるケースでは、アクセサを設けるメリットが明確ですので、こちらも検討不要です。
変更が予想されない時でも、アクセサ化するメリットがあるのか?が検討のポイントとなります。

【アクセサを設けるメリットのまとめ】
皆さんの意見のうち、【検討のポイント】的にメリットが明確なものを選択させて頂き、私が理解した内容で纏めてみました。(多少違っているかも知れません。その時はごめんなさい。)

raccyさん
メンバ変数へのアクセスは原則Read Onlyとするので、常にアクセサを使う。

私的な見解:
この基準(コーディング規約かも?)により、多少不慣れな人がいても比較的安全に開発を進めることができると言うメリットがあると感じます。
大勢の人がメンテナンスするようなプロジェクトで特に有用ですね。

catsforepawさんとyohhoyさん
変更が予想されないとはいえ可能性は0ではないので、アクセス時の処理変更の影響範囲が大きい場合、その影響を軽減するためにアクセサを設ける。
また、多くの場所からアクセスされる時はアクセサを設けるとデバッグしやすい。

私的な見解:
逆に、変更しても影響範囲が狭い場合はアクセサを設けるメリットが小さいのでpublicのままでも十分ということです。
そして、C++の場合は比較的熟練したプログラマが担当するケースも少なくないと思います。そのようなケースでは密結合部分についてpublicとして開示した方が、見通しも良くなりポインタ経由アクセス等、使えるテクニックも増えるのでより好ましいと感じます。

【私的な方針のまとめ】
私自身が担当しているプロジェクトは小規模・少数精鋭型が多いです。
そこで、下記方針を選択することにしました。
  • メンバ変数が少しの場所(数カ所程度?)からしかアクセスされないものについては、publicとする。
  • 外部に公開する部分も含め、これに該当しない場合はアクセサ化する。
あくまでも方針なのでそれなりに例外もあるとは思いますが、これで罪悪感を感じず(笑)にpublic定義できます。

ところで、何故にC++限定だったのか?
最初に「この話題はC++言語限定とさせて下さい。広げると収拾がつかなくなる予感がしますので。」と記載させて頂きました。いまいち明確に表現できてなかったのですが、今回の議論を通じてやっと言語化できました。

C++言語でのプログラミングでは、可読性やメンテナンス性も当然熟慮しますが、高速性もかなり重視しており、多少(場合によっては大きく)可読性/メンナナンス性を劣化させてでも速度を取る言語です。ポインタはその典型例と思います。更に参照とかconst参照とか右辺値参照とかもうパニック・レベルですね。まして、テンプレート・メタ・プログラミングと来た日にはorz。

この辺がスピードを犠牲にして可読性やメンテナンス性に注力しているJavaやC#のような言語と大きく使い方が異なる部分と思います。
その結果、アクセサを定義する/しないの判断基準もそれなりに異なっていることが予想できるため、C++での方針を形作りたい自分にとっては、言語を限定した方がよいということだったのです。

それでは、最後にもう一度、Stripeさんも含め、ご回答頂いた皆さんありがとうございました。
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 10

+6

private原理主義者な私は、常にprivateにしています。

まずもって、クラスの設計の仕方からpublicにするという行程が欠けています。

1. いいクラス名を考える。
2. クラスが提供するメソッドを考える。(C++であろうとメンバー関数などという言葉は使わない!)
3. メソッドを実現するためにはどのようなデータを内部に持つ必要があるかを考える。この内部データはメンバー変数として実装するが、外には見せないことが前提、つまりはprivateにする。
4. 他クラスとの連携で、内部データをそのまま渡した方がスムーズに行く場合のみ、getterを作ることを考える。
5. 他クラスとの連携で、内部データをピンポイントで書き換える事が実装上どうしても必要な場合は、本当に必要なのか脳内会議を開いて、それでも必要と結論ができたときのみ、setterをしぶしぶ作ることを考える。

これがいいのかは私にはわかりませんが、たぶん、プログラミングを始めた頃にJavaの色んな本でpublicは悪と教え込まれ、その後にRubyに洗脳されてしまったからだと思います。C++をはじめからやっておけば、違ったやり方をしていたのかも知れません。

なお、構造体は全く別枠で考えています。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/11/30 22:54

    なるほど、なんかカッコいいですね。
    そっか、デフォルトがRead Onlyなのですね。まずはアクセス制限から入るならアクセサが必要になります。であれば、多少の複雑化は許容できそうですね。
    多くの人がメンテナンスするケースで有用な方針のように感じます。

    私自身は、内部的に閉じた部分ではコーディング時の制約が少ない方を取ることが多いです。C++ですから、やはりスピード命的な部分があるので、ここでコピーが走らないか?とか、この処理コンパイル時にできないか?とかを結構考えています。なので、内部的に閉じた部分ではあまりRead Onlyにはしないです。
    外部へ公開する部分は、想定外の使い方を制限しておきたいので、不要な操作をできないように設計しますけど。

    キャンセル

  • 2015/12/02 00:15

    やっぱり、open-closed原則から考えてもそれがベターですよね。

    >C++ですから、やはりスピード命的な部分があるので
    のようにヘッダファイルにsetter/getterを書いてinline展開するぐらいなら最初からpublicでいいじゃんっていう考え方もあるんですよね。

    キャンセル

  • 2015/12/02 13:25

    > 最初からpublicでいいじゃんっていう考え方もあるんですよね。
    速度を上げるために無駄なコピーを防ぐことに注力したいから、Ready Onlyに煩わされたくないって感じです。内部的に閉じたクラスなら、それほど厳密にしなくても問題は起きにくいだろうと思ってます。

    また、C++で書かれたプログラムを不慣れなプログラマがメンテするケースって比較的少ないと思うのですよ。C++を使いこなせるようになるのってそれなりに経験を積まないと厳しいですから。
    ある程度の熟練者が対象なら、アクセス制限するより、密結合部のアクセス自由度は高い方がよいかなとも思ってます。

    キャンセル

checkベストアンサー

+3

例に挙げているような単純なアクセサは、作った方が良いと思われる積極的な理由がなければ、わざわざ作ったりはしません。内部的な(使用箇所が限定されるような)クラスでは作らないことが多いです。

ライブラリや共通部品として公開するような使用範囲の広いクラスでは、基本的には以下のような理由からアクセサを作ります。
  • デバッグがしやすくなる(テストコードを書いたり、ブレークポイントを置いたり)
  • セーフティガード(ポインタ経由で想定外の場所からメンバ変数を読み書きされないように)
  • アクセス時の処理を変更した場合の影響範囲の広さを考慮

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/11/29 10:40 編集

    ありがとうございます。

    > 例に挙げているような単純なアクセサは、(中略)作らないことが多いです。
    なるほど。私もそれが正しい姿と思うのですよ。背中を押して貰えてありがたいです。

    > デバッグがしやすくなる(テストコードを書いたり、ブレークポイントを置いたり)
    おお、そういえばそうですね。この可能性は気がついてませんでした。
    C言語時代に多用してたアドレスブレークに気を取られていたようです。
    多くの場所からアクセスされるような変数の場合はアクセサ経由にしておくとデバッグに便利そうですね。
    同時に処理追加時の影響も軽減できますし。

    > セーフティガード(ポインタ経由で想定外の場所からメンバ変数を読み書きされないように)
    なるほど。アクセサを使うことでアドレスを隠せますね。

    キャンセル

+3

>構造体(データを保持することが主目的)として設計したクラスであれば良いのですが
と述べているようにそのような設計であれば構造体でいいんじゃないですか?

メソッドの追加が必要になるということは再設計になるので、たとえばBar構造体をメンバに含むクラスFooを新しく作ればいいだけだと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/12/01 06:40

    taroさんの例ですが、リングバッファのデータカウントがそれだと思いついやのですが、けっこうありますよね。

    キャンセル

  • 2015/12/01 21:21

    > ある処理を行っている間はメンバを書き換えて欲しくない...変更がクラス内で済む

    それってつまりこういうことですか?
    class Hoge {
    private:
    int value;
    public:
    int getValue() { return value; }
    void setValue(int v) {
    lock(this);
    value = v;
    unlock(this);
    }
    void run() {
    lock(this);
    // valueを使った何らかの処理
    unlock(this);
    }
    };

    でも、これだと、

    void thread1() {
    Hoge obj = getObj();
    obj.setValue(123);
    obj.run();
    }

    void thread2() {
    Hoge obj = getObj();
    obj.setValue(456);
    obj.run();
    }

    この2つをマルチスレッドで動かした時に破綻すると思います。


    > リングバッファのデータカウント

    それって、そもそもアクセッサになるんですか?違うのでは?

    キャンセル

  • 2015/12/01 22:50

    >この2つをマルチスレッドで動かした時に破綻すると思います。
    run()メソッド内でvalueの値が変わらないので目的は達成しているのでは?

    >それって、そもそもアクセッサになるんですか?違うのでは?
    リングバッファはあくまでも一例で、質問の主題はメンバをpublicにするかアクセサを作るかということなので違わないような気がします。

    質問の主題から逸れてるので排他制御の話題は止めませんか?

    キャンセル

+2

今現在、読み書きで使うのならpublicなメンバにして、アクセサの必要が生じた時点で、privateにしてアクセサに変更すれば宜しいかと思います。
将来の仕様変更においても、コンパイラが修正場所を漏れなくエラーメッセージとして報告してくれますから、人間の注意力に頼らない非常に安全な修正ができるはずです。
私は、積もり積もった複雑さ、猥雑化(コードの読みにくさを)の方を問題にします。
class int_acc {
private:
        int aaaa;
public:
#if 1
        int& operator=(int i) {
                this->aaaa = i;
                return  this->aaaa;
        }
#endif
        operator int(){
                return this->aaaa;
        }
};
class cfoo {
public:
        int_acc abc;
};
void bbbb()
{
        cfoo a;
        int i,j,k;
        a.abc = 3;
        i = a.abc;
}
これはアクセサ?

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/12/01 00:09

    代入演算子とキャスト演算子ですね。これらはアクセサとは言わないです。
    確かにこの例ではメンバ変数をアクセスしてますが、この方式ですと複数のメンバ変数に対応できないからです。

    アクセサを使うメリットですが、例えば、私の例で、int mMember;をdouble mMember;へ変更したとします。
    そして、例えば、int getMember() {return round(mMember);}としておくことで、getMember()を使っている他のクラス側は少なくとも形式的には変更する必要がありません。
    更に、真の意味で変更不要であることまでテストしてからリリースすれば、他のクラスの開発者は何もしないのでよいので、非常に助かります。
    もしも、Fooが非常に汎用なライブラリで、幸いにも数万人の方に使って頂けていたような場合、その数万人がバージョンアップ時にプログラム修正不要なので、非常に効果的なのです。

    キャンセル

  • 2015/12/01 06:32 編集

    Chironianさんがこうだ!って考えているアクセサってどんなものですか?
    初心者質問者さんにはこんなこと聞きませんが、Chironianさんなら答えられる気がします。
    そしてその答えに、この議論での答えがあるのではないでしょうか?
    自分の意見を言わないのはズルイので申し上げますが、私は、
    C++にはアクセサなんてなんて無いのだけれど他の言語からの概念を真似しててアクセサっぽいものも実装できるだけなんじゃないかと考え出しました。

    キャンセル

  • 2015/12/01 11:11

    あまり厳密ではないですが、ざっくりメンバ変数に対して1対1で読出/書込する関数をアクセサと呼んでます。

    > そしてその答えに、この議論での答えがあるのではないでしょうか?

    なるほど。ちょっと考えてみました。

    > C++にはアクセサなんてなんて無いのだけれど他の言語からの概念を真似しててアクセサっぽいものも実装できるだけなんじゃないかと考え出しました。

    アクセサはオブジェクト指向プログラミングでコードを書く時の特定のパターンです。
    C++はオブジェクト指向プログラミングに対応していますので、アクセサを書けますし、書くことが推奨されるケースがあります。

    オブジェクト指向プログラミングでは内部の細かい実装を隠して、外部とは特定の関数を使ってI/Fするバターンが良く使われます。というか、内部の細かい実装を開示するべきではないので関数でI/Fするべきと言う主張をよく聞きます。かなり多くのケースで正しい主張と思います。

    しかし、特定の変更要求や問い合わせについて、メンバ変数をそのままやり取りすれば十分ケースがあります。そのような時でもアクセサを使うべきか?について議論の余地があると思うのです。

    今回の議論を通じてなんとなく感じているのですが、恐らくオブジェクト指向的に設計したクラスといえども、構造体のようにデータを保持する役割を果たすケースがあり、その際にもオブジェクトとしての指針を適用するのが妥当なのか?ってことかも知れません。

    外部I/Fするクラスについては、「オブジェクト指向と構造体が入り混じったクラス」を設計するとやばい予感がします。オブジェクトの内部データを他の人が自由に触れるのはやはり危険な気がします。
    しかし、内部的に閉じたクラスではこれを許した方が生産性をあげつつ、高速なプログラムを書けるのではないかと感じてます。

    ああ、そう考えると、内部的に閉じたクラスは全部privateとし、相互friend指定するのも1つかも? ちょっと極端すぎるので穴が一杯ありそうですが。

    キャンセル

+2

publicメンバは、constの場合のみですね…
「アクセサを用意する」という方針以外考えられません。

理由はいろいろ考えられますが、いくつか書いてみると…

・mMemberの型を変更する可能性
・mMemberの正当性をチェックしたい
・暗黙の型変換を防ぐ

#Fooがconstである場合、例のままだと読み出しアクセスさえできませんね。
    int getMember() const { return mMember; }

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/12/01 14:51 編集

    回答ありがとうございます。

    > publicメンバは、constの場合のみですね…
    なるほど。実はこの質問を発したきっかけが、内部的に閉じたクラスのconstメンバは流石にアクセサ要らんよなって感じてたことでした。
    ところで、公開するクラスでもconstメンバはgetを用意しないことってありますか?

    > 暗黙の型変換を防ぐ
    これってどうやるのですか?

    > Fooがconstである場合、例のままだと読み出しアクセスさえできませんね。
    あっ、そうですね。時々やっちゃいます。ご指摘ありがとうございます。

    キャンセル

  • 2015/12/02 10:52

    > 暗黙の型変換を防ぐ
    vga640x480さんの仰る内容を私なりに勝手に解釈すると...
    キャストをせず、それぞれの型に変換済みの値を返すアクセッサ(get)を用意するとの趣旨かと思いました。
    class TimeStamp
    {
    private:
    double _value;
    public:
    double getValue() { return _value; }
    time_t getValue() { return ...; }
    SYSTEMTIME getValue() { return ...; }
    ...
    //もしくは
    operator double() { return ...; }
    operator time_t() { return ...; }
    operator SYSTEMTIME() { return ...; }
    }

    キャンセル

  • 2015/12/02 11:28

    hskさん、情報ありがとうございます。
    しかし、やはりよく分かりません。

    TimeStamp ts(...);
    float value = ts.getValue();
    で暗黙の型変換されると思います。

    仮に_valueをpublicで宣言していた場合も
    float value = ts._value;
    で全く同じ暗黙の型変換が発生するように思うのです。

    C++って本当に奥が深いので、私が知らない/気がついていない何かのような気がするのですが、それがよく判らなくて。

    キャンセル

  • 2015/12/02 12:38 編集

    確かに、プリミティブな型の事前定義された代入元・先の組み合わせや、標準的なライブラリで提供される変数型のそれであれば、受け取り側の型に合わせた暗黙的な変換が(まさに暗黙的に)行われますね。

    アンチパターンみたいなものを示すことができればよいのですが、なかなか思いつかないとすると、結構曖昧か決め付けて的な動機なのかも...私。。
    ご質問の
    >処理を追加する可能性がほぼ想定されないなら...
    により、クラス側も、クラスを利用する側も本当に起こらないならば、どちらでもよいのかもしれません。程度問題かと思います。
    そうで無い場合(どちらかが変化するとき)、アクセッサを設けたほうが「楽」だなと思います(他者にコード保守を引き継ぐかもしれないですし...)が、受け取り側や中継時に変換関数を用意する方法もありますし、いずれにせよ、私の場合はメソッドを介させて、なるべく結合度を疎にさせるようにしています。

    しかし、アンチパターンのようなものを示せればよいのですが、すぐに思い浮かばないとすると、そのあたり結構曖昧か決めつけ的なのかな...私も。。

    キャンセル

+1

私の場合クラスと構造体の使い分けは「データ保持以外に機能を持たせるかどうか」という点です。

データ保持用のために設計した場合、構造体として定義してメンバが特殊でどうしても初期化処理が必要な場合はコンストラクタを書くこともありますが、基本的にはメンバ関数は持たせません。

データ保持以外になんらかの演算をさせたい場合、クラスとして設計してメンバ変数を隠蔽します。単純なアクセサも書きますが、get/setともに、範囲チェックなどの処理も含めたものにすることが多いです。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/11/29 10:45

    > どうしても初期化処理が必要な場合はコンストラクタを書くこともありますが、基本的にはメンバ関数は持たせません。
    私も同じ方針です。std::vector<>に入れる時等、コンストラクタだけは必要になりますからね。

    > データ保持以外になんらかの演算をさせたい場合、クラスとして設計してメンバ変数を隠蔽します。
    そうなんですよ。私もなんとなくそのようにしてしまいます。
    でも、積極的な理由もないのに単純なアクセサを設けるのは、シンプルイズベスト原則に照らすと宜しくないのでちょっと悩ましく感じてます。

    キャンセル

+1

構造体(データを保持することが主目的)として設計したクラスであれば良いのですが、オブジェクト指向的に設計したクラスでは抵抗を感じてしまい、躊躇してます。 
個人的には、言及されているものとおおよそ同じ指針をとっています。使い分けの線引きラインとしては、「責任分界の強度」を用いています。外部仕様のようにインタフェースがはっきりとしている場合には、厳密な使い分けを心がけますが、内部実装のように結合度が高い場合には、曖昧な使い分けになります。

ついでにstructとclassの使い分けもすることが多いです。言語仕様的にはデフォルトでpublic/privateという差しかありませんが、コンパイラでは無く人(プログラマ)に意味を伝える目的で使い分けています。


単なるデータの集まりを表す場合は、publicメンバをもつstructとして宣言します。基本的にメンバ関数は実装しません。
  • データコンテナ型のようにある程度複雑なデータ構造を表現する場合、データメンバを直接privateで公開せずに隠蔽し、より高位の操作をアクセサとしてpublicメンバ関数化することがあります。
  • ライブラリ内部実装向け入れ子(Inner)クラス等では、publicメンバ+実装上便利なメンバ関数を実装することがあります。

いわゆるオブジェクト指向的なクラスの場合は、全て非publicメンバをもつclassとして宣言します。データメンバへは必ずアクセサ経由とします。
  • 上記繰り返しとなりますがライブラリ内部実装向けのクラス等では、(手抜きのため)一部をpublicメンバ化することもあります。


余談ですが、アクセサのメリットとして挙がっている、排他制御(スレッドセーフ性)の後付けはおすすめしません。あるクラスがスレッドセーフ操作を提供するか否かは、決して内部の実装詳細などではなく、そのクラス外部仕様の一部であるべきです。後付けでの排他制御実装は論理破綻、もしくは実行時の不安定動作リスクに繋がります。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/11/30 13:16

    ご回答、ありがとうございます。

    >外部仕様のようにインタフェースがはっきりとしている場合には、厳密な使い分けを心がけますが、内部実装のように結合度が高い場合には、曖昧な使い分けになります。

    了解です。yohhoyさんも内部実装ではpublicデータメンバも有りなのですね。
    多くの人が同様な方針のようでホッとしています。

    > 単なるデータの集まりを表す場合は、publicメンバをもつstructとして宣言します。基本的にメンバ関数は実装しません。
    (後略)

    積極的にstructを使い分けている人が複数いるって心強いです。
    昔、あるJavaウィザードの人がstructの概念を理解してくれず、データを保持することが目的のクラスにメンバ関数を入れないことを納得してくれず苦労したことがあります。
    たぶん、C++とJavaの成り立ちの相違が原因かなと感じてます。

    > 排他制御(スレッドセーフ性)の後付けはおすすめしません。

    全くその通りですね。排他制御は設計的に対応しておかないと、再現性が非常に低いのでマジ辛いことになりますから。
    マルチスレッド対応のライブラリの場合は、必要な部分は最初から排他制御しておくべきと私も思います。

    外部的にはマルチスレッド非対応で内部的に高速化等のためにマルチスレッド化するようなケースでは、内部I/Fで閉じると思います。過去、そのようなケースを担当したことがあります。
    その時も、基本設計的には自社側プログラムで対応できるように設計しておき、使用する他社ライブラリがスレッド安全ならばそちらに任せ、そうでない時はこちらで対応しました。
    当時はまだC++をベターCとして使っていたのでアクセサ云々は意識してませんでしたけどね。

    キャンセル

  • 2015/11/30 17:35 編集

    確かにJavaメインの人には「アクセサ以外あり得ない」と言われそうですね… 考え方はJavaBeansあたりから来ているような気もします(適当)

    Java言語圏のgetter/setter文化については http://d.hatena.ne.jp/Nagise/20141010/1412930502 などが面白いです。

    対象のプログラミング言語が違えば思想もちがってくるので、その辺は柔軟に対応してほしい/すべきではありますが、実際にはなかなか難しいかも。

    キャンセル

  • 2015/11/30 18:52

    > 確かにJavaメインの人には「アクセサ以外あり得ない」と言われそうですね
    ですね。JavaとC++では同じオブジェクト指向でも雰囲気というか文化の相違を感じます。

    情報ありがとうございます。URL先の文書、読み応えありますね。
    IDEがGUI部品を取り扱えるようにするための仕様がJavaBeansで、それがアクセサを要求するのですね。なるほど。

    キャンセル

+1

ベストプラクティスパターン
(ケント・ベック著)
という本がありまして、
本で使用している言語がC++
ではないので申し訳ないのですが、
インスタンス変数を直接アクセス
すべきなのか、それともメソッドを
必ず介すのがいいのかという
議論が紹介されていました。

どちらが良いとか悪いとかではなく、
どちらも一長一短があり、
難しい議論です。ます。

お読みになってみては
いかがでしょうか?

蛇足ながら、
私個人はこの本を読了し、
必ずメソッドを介したほうが
良いと結論を出しました。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/12/01 21:45

    情報ありがとうございます。

    > ベストプラクティスパターン
    SmallTalkの本なのですね。ばりばりのオブジェクト指向っぼいですね。
    その世界でも、「どちらが良いとか悪いとかではなく、どちらも一長一短があり、難しい議論です。」というのはちょっと驚きです。オブジェクト指向的には、private一択と思ってました。

    キャンセル

+1

こんにちは。もしかしてすでに似た趣旨のご回答をされている方もおられるかもしれませんが...

C++では、クラスのメンバーにおいてアクセス修飾子を指定しない場合 private になりますので、クラスについて基本的にはメンバーを隠匿するような使用法を言語設計者は意図しているのだろうと思います。
私の経験的にですが、vga640x480さんが仰られる内容や、デバッグのしやすさなどから、アクセッサを経由させたほうが保守性が向上しています(改修などで、あとから楽)。
たとえば、メンバー変数の型のほうを変更した場合も、公開する変数型を同じままに保つことができます。

オブジェクトやデータをさらけ出してひとかたまりとして扱いたい場合は、既定のアクセスレベルがpublicである、構造体を私なら使用します(class から structにコードを変えます)。

ご質問にもどりますと、私なら躊躇せずメンバー変数は隠匿し、アクセッサを定義します。すべて公開したいデータ(オブジェクトや変数)の集まりなら、構造体にします。すぐ捨てるサンプル的なコードならともかく、面倒ですがクラスを定義すると同時(早い段階)でアクセッサも用意します。

余談ですが、C++でも.NET Frameworkのマネージドアプリケーションを作れますけれども、オブジェクト間のバインド機構を使うときにプロパティ(アクセッサ)である必要がある(フィールド(メンバー変数)は使用不可)だったりするので、 .NET 開発者もclassオブジェクトに関しては同様の考えを持っているのかな...と感じます。いちいちアクセッサを書くことは面倒かもしれませんが、IDEがずいぶん進化していて助けられます。

趣旨からずれていたらすみません。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/12/02 11:51 編集

    ご回答ありがとうございます。

    > メンバー変数は隠匿し、アクセッサを定義します。

    私もどちらかと言うとそうするのですが、変更の可能性がほとんど考えられないような場合にアクセサを導入することによるソースの複雑化を正当化できるのはどんな時なのか? という疑問なのです。

    私自身はシンプルイズベストと考えています。つまり、無意味な複雑化は害悪と思うので、何らかの複雑さを導入する場合は、そのメリットが必要と思います。
    なのに私自身、この方針に反して、将来的な変更が考えられないようなメンバ変数をpublicにすることに躊躇してしまうのです。

    そこで、他の方のご意見も参考にさせて頂いて、自分なりの方針を固めたいと思っています。
    多くの方のご意見を頂き、かつ、議論にお付き合い頂き、だんだん見えてきています。
    どうもC++言語のマルチパラダイムな部分も影響しているような気がしてます。
    私なりの方針がある程度まとまったら、報告させて頂こうと思います。

    キャンセル

-2

C++に限った話ではありませんが、構造体を使いたければ構造体を定義すればいいだけだし、オブジェクトを使いたければオブジェクトを定義するだけです。
結局のところ、あなたは何をしたいんですか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/11/30 18:46

    あなたの質問が方針を尋ねていることに疑義があります。

    既に他の回答者から出ている、
    「排他制御する時に、アクセッサを実装する」
    「アクセッサの必要が生じた時に、アクセッサを実装する」
    というのは、方針ではありません。
    ケースバイケースの一例を示しているものだと思います。
    それをあなたが、「方針」と言っているのです。

    方針というのは、例えば、
    「全てのプロパティの実装方式をアクセッサに統一することとする」
    というようなものを言うのだと思います。

    しかし、今回あなたは、状況に応じて「publicなメンバ変数」と「アクセッサ」を使い分けようとしていますよね?
    その、"状況に応じて使い分ける"っていうのは、ようするにケースバイケースということなんじゃないですか?

    ケースバイケースな話なのですから、最終的に「あなたが何をしたいのか?何を作ろうとしているのか?」という所に集約されるのだろうと思います。

    キャンセル

  • 2015/11/30 20:03

    なるほど。Stripeさんは、ケースバイケースなら具体的な状況が分からないと分類できないとお考えなのですね。
    私はそうは思いません。抽象的な状況でも分類できる方針が存在し得ると考えています。その方針をお持ちの方に、それを教えて欲しいと質問させて頂きました。
    この点で決定的に考え方が異なりますので、お互いにスルーするしかないと思います。

    キャンセル

  • 2015/11/30 23:14

    その「抽象的な状況でも分類できる方針」のことを、あなたは既に知っているんじゃないんですか?
    しかし、それは「抽象的な方針」であったため、けっきょく具体的なところがよく分からないという状況なのだと思います。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.10%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる