私はもともとC#の世界の出身で、C++を学んでいます。 C++で関数を取得および設定することについて疑問に思っていました。 C#では、これらの使用が非常に人気があり、Visual Studioなどのツールは、非常に簡単かつ迅速に実装できるようにすることで使用を促進します。ただし、これはC++の世界では当てはまらないようです。
C#2.0コードは次のとおりです。
_public class Foo
{
private string bar;
public string Bar
{
get { return bar; }
set { bar = value; }
}
}
_
または、C#3.0の場合:
_public class Foo { get; set; }
_
人々は、その中のポイントは何ですか?必要に応じて、パブリックフィールドを作成し、後でそれをプロパティにするだけではどうでしょうか。正直なところ、私は実際にはわかりません。私は何度も見たことがあるので、私はそれを良い習慣から外しています。
今はそうすることに慣れているので、習慣をC++コードに引き継ぐ必要があるように感じますが、これは本当に必要ですか? C#ほど頻繁には行われません。
とにかく、ここに私が集めたものからのC++があります:
_class Foo
{
public:
std::string GetBar() const; // Thanks for the tip Earwicker.
void SetBar(std::string bar);
private:
std::string bar;
}
const std::string Foo::GetBar()
{
return bar;
}
void Foo::SetBar(std::string bar)
{
// Also, I always wonder if using 'this->' is good practice.
this->bar = bar;
}
_
さて、私にとっては、それは多くの脚の仕事のように思えます。 Visual Studioのツールを使用することを検討すると、C#の実装は文字通り実装に数秒かかり、C++では入力にかなり時間がかかりました。
_class Foo
{
public:
std::string Bar;
}
_
私が収集したものから、これらは利点です:
そして欠点:
なぜ 投票数の少ない回答 を選択したのですか?実際、私は veefuの答え ;を選択することに非常に近かった。しかし、私の個人的な意見(明らかに物議をかもしている)は、その答えがプリンを生んだということです。
一方、私が選んだ答えは、両者を主張しているようです。ゲッターとセッターは過度に使用すると悪だと思います(つまり、必要がなく、ビジネスモデルを壊すという意味です)が、なぜGetBalance()
?
確かに、これはPrintBalance()
;よりもはるかに用途が広いでしょう。クラスが私に望んでいたのとは別の方法でユーザーに表示したい場合はどうすればよいですか?さて、ある意味ではGetBalance()
は「ゲッターとセッターは良い」と主張するほど適切ではないかもしれません。なぜなら(またはshould n't)付属のセッターがあり、それについて言えば、SetBalance(float f)
と呼ばれる関数は、アカウントのクラス外で操作する必要があることを関数の実装者に示唆するため、(私の意見では)悪い可能性があります。これは良いことではありません。
アクセサーの提供は、C#よりもC++の方が重要だと主張します。
C++には、プロパティの組み込みサポートがありません。 C#では、ユーザーコードをほとんど変更せずに、パブリックフィールドをプロパティに変更できます。 C++では、これは harder です。
入力を減らすために、簡単なセッター/ゲッターをインラインメソッドとして実装できます。
class Foo
{
public:
const std::string& bar() const { return _bar; }
void bar(const std::string& bar) { _bar = bar; }
private:
std::string _bar;
};
そして、忘れないでください ゲッターとセッターはやや悪です。
議論の余地があるという危険を冒して、「パターンのホルブ」を読んでいるときに私が最初に出会った反対の視点を支持します。それは非常に挑戦的な視点でしたが、考えてみると意味がありました。
ゲッターとセッターは悪です
ゲッターとセッターの使用は、オブジェクト指向設計の基本であるデータの抽象化とカプセル化に反しています。ゲッターとセッターを使いすぎると、コードのアジャイル性が低下し、長期的に維持しやすくなります。最終的には、クラスの基礎となる実装を公開し、実装の詳細をクラスのインターフェイスにロックします。
「std :: string Foo :: bar」フィールドをstd :: stringから別の文字列クラスに変更する必要があると想像してください。たとえば、より最適化されているか、異なる文字セットをサポートしています。プライベートデータフィールド、ゲッター、セッター、およびこれらのゲッターとセッターを呼び出すこのクラスのすべてのクライアントコードを変更する必要があります。
「データを提供」および「データを受信」するようにクラスを設計するのではなく、「操作を実行する」または「サービスを提供する」ように設計します。 「GetBar」関数を書いている理由を自問してください。そのデータで何をしていますか?おそらく、そのデータを表示しているか、何らかの処理を行っています。このプロセスは、Fooのメソッドとしてより適切に公開されていますか?
これは、ゲッターとセッターに目的がないと言っているわけではありません。 C#では、それらの使用の基本的な理由はVisual Studio GUIデザインIDEとのインターフェースであると考えていますが、C++で作成していることに気付いた場合は、戻ってデザインを見て、何かを確認するのがおそらく最善です不足している。
説明のために例をモックアップしてみます。
// A class that represents a user's bank account
class Account {
private:
int balance_; // in cents, lets say
public:
const int& GetBalance() { return balance_; }
void SetBalance(int b) { balance_ = b; }
};
class Deposit {
private:
int ammount_;
public:
const int& GetAmount() { return ammount_; }
void SetAmmount(int a) { _balance = a; }
};
void DoStuffWithAccount () {
Account a;
// print account balance
int balance = a.GetBalance();
std::cout << balance;
// deposit some money into account
Deposit d(10000);
a.SetBalance( a.GetBalance() + d.GetValue());
}
これが非常に不十分に設計されていることを確認するのにそれほど時間はかかりません。
クライアントコードDoStuffWithAccountがアカウントの残高を実装するために使用したデータ型にバインドされるようになったため、ゲッターとセッターは問題の修正をより困難にします。
それで、このコードを渡して、改善できるものを見てみましょう
// A class that represents a user's bank account
class Account {
private:
float balance_;
public:
void Deposit(float b) { balance_ += b; }
void Withdraw(float w) { balance_ -= w; }
void DisplayDeposit(std::ostream &o) { o << balance_; }
};
void DoStuffWithAccount () {
Account a;
// print account balance
a.DisplayBalance(std::cout);
// deposit some money into account
float depositAmt = 1000.00;
a.Deposit(depositAmt);
a.DisplayBalance(std::cout);
}
「フロート」は正しい方向への一歩です。確かに、内部型を 'float'に変更しても、ゲッター/セッターイディオムを引き続きサポートできます。
class Account {
private:
// int balance_; // old implementation
float balance_;
public:
// support the old interface
const int& GetBalance() { return (int) balance_; }
void SetBalance(int b) { balance_ = b; }
// provide a new interface for the float type
const float& GetBalance() { return balance_; } // not legal! how to expose getter for float as well as int??
void SetBalance(float b) { balance_ = b; }
};
ただし、intを使用するコードとfloatを使用する新しいコードの両方をサポートする必要があるため、ゲッター/セッターの配置が作業負荷を2倍にし、問題を複雑にしていることに気付くのに時間がかかりません。入金機能を使用すると、入金するタイプの範囲を少し簡単に拡張できます。
アカウントのようなクラスはおそらく最良の例ではありません。アカウントの残高を「取得」することはアカウントにとって自然な操作だからです。ただし、全体的なポイントは、ゲッターとセッターに注意する必要があるということです。すべてのデータメンバーに対してゲッターとセッターを記述する習慣を身に付けないでください。注意を払わなければ、実装を公開してロックするのは非常に簡単です。
あなたの例では:
_class Foo
{
public:
const std::string GetBar(); // Should this be const, not sure?
_
あなたはおそらくこれを意味します:
_std::string GetBar() const;
_
const
を最後に置くことは、「この関数は呼び出されたFooインスタンスを変更しない」ことを意味するため、純粋なゲッターとしてマークします。
純粋なゲッターはC++で頻繁に発生します。 _std::ostringstream
_の例は、str()
関数です。標準ライブラリは、多くの場合、ゲッター/セッター関数のペアに同じ関数名を使用するパターンに従います-str
は再び例です。
入力するのが面倒で、それだけの価値があるかどうかについては、奇妙な質問のようです!クライアントに何らかの情報へのアクセスを許可する必要がある場合は、ゲッターを提供します。そうしないなら、しないでください。
C#やJavaにあるような、これに関する厳密な規則はありません。多くのC++プログラマーは、変数を公開するだけで問題を解決できます。
他の答えが言ったように、多くの場合、setメソッドを取得する必要はありません。
ただし、それらを作成する場合、必要以上に入力する必要はありません。
class Foo
{
public:
std::string Bar() const { return bar; }
void Bar(const std::string& bar) { this->bar = bar; }
private:
std::string bar;
};
クラス内で関数をインラインで宣言すると、入力が不要になり、関数をインライン化することをコンパイラに示唆します。そして、C#の同等物よりも多くのタイピングはありません。注意すべきことの1つは、get/setプレフィックスを削除したことです。代わりに、2つのBar()オーバーロードがあります。これはC++ではかなり一般的です(結局、引数を取らない場合はゲッターであり、引数を取る場合はセッターです。それを伝えるために名前は必要ありません)、そして入力をもう少し節約します。
[編集]セッターはパラメーターを検証し、不変条件を強制する必要があることを強調する必要があるようです。そのため、通常はここほど単純ではありません。 [/編集]
余分なタイピングのため、すべてではありません。 Visual Assistから「フィールドをカプセル化」できるようになったので、今では頻繁に使用する傾向があります。
クラス宣言でデフォルトのセッター/ゲッターのみをインラインで実装する場合、レッグワークはそれ以上ではありません(ただし、より複雑なセッターは本体に移動します)。
いくつかのメモ:
constness:はい、ゲッターはconstである必要があります。ただし、値で返す場合、戻り値をconstにすることは役に立ちません。潜在的に複雑な戻り値の場合は、const&を使用できます。
std::string const & GetBar() const { return bar; }
セッターチェーン:多くの開発者は、セッターを次のように変更することを好みます:
Foo & SetBar(std::string const & bar) { this->bar = bar; return *this; }
これにより、複数のセッターを呼び出すことができます:
Foo foo;
foo.SetBar("Hello").SetBaz("world!");
しかし、それは良いこととして普遍的に受け入れられていません。
__ declspec(property):Visual C++はこの非標準の拡張機能を提供するため、呼び出し側はプロパティ構文を再び使用できます。これにより、クラスでの作業が少し増えますが、呼び出し元のコードは見やすくなります。
そのため、結論として、もう少し脚注がありますが、C++で行うべき決定はほんの一握りです。典型的な ;)
自分のコードでゲッターとセッターを使用することはほとんどありません。 Veefuの答え 私には良さそうです。
ゲッターおよび/またはセッターを持つことを主張する場合、マクロを使用してボイラープレートを削減できます。
#define GETTER(T,member) const T& Get##member() const { return member; }
#define SETTER(T,member) void Set##member(const T & value) { member = value; }
class Foo
{
public:
GETTER(std::string, bar)
SETTER(std::string, bar)
private:
std::string bar;
}
データメンバーの取得と設定quaデータメンバー:Bad。
抽象化の要素の取得と設定:Good。
銀行の例のAPIデザインの観点から、Get/Setに対する議論は注目されています。ユーザーがビジネスルールに違反することを許可する場合は、フィールドまたはプロパティを公開しないでください。
ただし、フィールドまたはプロパティが必要であると判断したら、常にプロパティを使用します。
C#の自動プロパティは非常に使いやすく、フィールドでは機能しないがプロパティを必要とする多くのシナリオ(データバインディング、シリアル化など)があります。
取得および設定は、任意の言語で使用する必要がある場合に人々に課される苦痛です。
エッフェルは、答えを得るために提供しなければならない情報の量が異なる場合に、より良くなります-0のパラメーターを持つ関数は、メンバー変数にアクセスするのと同じであり、それらの間で自由に変更できます。
インターフェイスの両側を制御する場合、インターフェイスの定義はそれほど大きな問題ではないようです。ただし、C++の一般的なケースのように実装の詳細を変更してクライアントコードの再コンパイルを行う場合、これを可能な限り最小限に抑えることができます。そのため、 pImpl およびget/setは、このような損傷を回避するためにパブリックAPIでさらに使用されます。
変数値に制約がある場合、GetメソッドとSetメソッドは便利です。たとえば、多くの数学モデルでは、特定の浮動変数を範囲[0,1]に保つための制約があります。この場合、GetおよびSet(特にSet)は素晴らしい役割を果たします:
class Foo{
public:
float bar() const { return _bar; }
void bar(const float& new_bar) { _bar = ((new_bar <= 1) && (new_bar >= 0))?new_bar:_bar; } // Keeps inside [0,1]
private:
float _bar; // must be in range [0,1]
};
また、一部のプロパティは、読み取る前に再計算する必要があります。そのような場合、すべてのcicleを再計算するのに多くの不必要な計算時間がかかる場合があります。したがって、それを最適化する方法は、代わりに読み取り時にのみ再計算することです。そのためには、変数を読み取る前に更新するためにGetメソッドをオーバーロードします。
それ以外の場合、入力値を検証したり、出力値を更新したりする必要がない場合、プロパティを公開することは犯罪ではなく、それに沿って進めることができます。
COMコンポーネントを開発している場合、はい、非常に人気があります。
C++/CLIをC++のバリエーションとして使用する場合、言語でネイティブプロパティがサポートされるため、次を使用できます。
property String^ Name;
これはと同じです
String Name{get;set;}
c#で。 get/setメソッドをより正確に制御する必要がある場合は、次を使用できます。
property String^ Name
{
String^ get();
void set(String^ newName);
}
ヘッダーに
String^ ClassName::Name::get()
{
return m_name;
}
void ClassName::Name::set(String^ newName)
{
m_name = newName;
}
.cppファイル内。すぐに思い出すことはできませんが、getメソッドとsetメソッド(public/privateなど)に対して異なるアクセス許可を持つことができると思います。
コリン