シンプルな C++ ゲッター/セッター



最近、ゲッターとセッターを次のように書いています (注:実際のクラスはゲッター/セッターでより多くのことを行います):


struct A {
const int& value() const { return value_; } // getter
int& value() { return value_; } // getter/setter
private:
int value_;
};

これにより、次のことが可能になります:


auto a = A{2}; // non-const object a
// create copies by "default" (value always returns a ref!):
int b = a.value(); // b = 2, is a copy of value :)
auto c = a.value(); // c = 2, is a copy of value :)
// create references explicitly:
auto& d = a.value(); // d is a ref to a.value_ :)
decltype(a.value()) e = a.value(); // e is a ref to a.value_ :)
a.value() = 3; // sets a.value_ = 3 :)
cout << b << " " << c << " " << d << " " << e << endl; // 2 2 3 3
const auto ca = A{1};
const auto& f = ca.value(); // f is a const ref to ca.value_ :)
auto& g = ca.value(); // no compiler error! :(
// g = 4; // compiler error :)
decltype(ca.value()) h = ca.value(); // h is a const ref to ca.value_ :)
//ca.value() = 2; // compiler error! :)
cout << f << " " << g << " " << h << endl; // 1 1 1

このアプローチでは、次のことはできません:



  • セッターの入力を検証します (これは大きな問題ですが)、

  • const メンバー関数で値を返す (コンパイラに const オブジェクトへの代入をキャッチさせたいため:02 )。更新:以下の cluracan の回答を参照してください。




  • ほとんどの場合、それは必要ありません

  • これにより、クラスの実装の詳細をインターフェイスから切り離すことができます。これはまさに私が望んでいることです。


例:


struct A {
const int& value(const std::size_t i) const { return values_[i]; }
int& value(const std::size_t i) { return values_[i]; }
private:
std::vector<int> values_;
// Storing the values in a vector/list/etc is an implementation detail.
// - I can validate the index, but not the value :(
// - I can change the type of values, without affecting clients :)
};

質問に移りましょう:



  • 私が見落としているこのアプローチの他の欠点はありますか?

  • 好まれる理由:

    • 異なる名前の getter/setter メソッド?

    • パラメータとして値を渡していますか?
      入力を検証するためだけですか?それとも他に主な理由がありますか?



答え:



  • 一般的に、アクセサー/ミューテーターをまったく使用することは、クラスのパブリック インターフェイスが不完全であるという設計臭です。一般的に言えば、単純に get/set するのではなく、意味のある機能を提供する便利なパブリック インターフェイスが必要です (これは、C で構造体と関数を使用するよりも 1 ~ 2 ステップ優れています)。ミューテーターを書きたいときはいつでも、最初にアクセサーを書きたいと思うことがよくあります。 .

  • イディオムに詳しい人は、そのような機能を期待する準備ができていない可能性があるため、メンテナーがコードを理解する時間が増えます。

  • 同じ名前のメソッドは public メンバーとほぼ同じです:その場合は public メンバーを使用してください。メソッドが 2 つの異なることを行う場合は、2 つの異なる名前を付けてください。

  • 非 const 参照によって返される「ミューテーター」は、誰かがメンバーへのエイリアスを隠し、後で存在することに依存するという、さまざまなエイリアスの問題を可能にします。別のセッター関数を使用することで、人々があなたのプライベート データにエイリアスを作成するのを防ぎます。


いくつかのコードの回答


struct A {   const int&
value() const { return value_;
} // getter
int&
value()
{ return value_;
} // getter/setter private: int value_;
};
auto a = A{2};
// non-const object a // create copies by "default" (value always returns a ref!): int b = a.value();
// b = 2, is a copy of value :) auto c = a.value();
// c = 2, is a copy of value :) // create references explicitly: auto&
d = a.value();
// d is a ref to a.value_ :) decltype(a.value()) e = a.value();
// e is a ref to a.value_ :) a.value() = 3;
// sets a.value_ = 3 :) cout <<
b <<
" " <<
c <<
" " <<
d <<
" " <<
e <<
endl;
// 2 2 3 3 const auto ca = A{1};
const auto&
f = ca.value();
// f is a const ref to ca.value_ :) auto&
g = ca.value();
// no compiler error! :( // g = 4;
// compiler error :) decltype(ca.value()) h = ca.value();
// h is a const ref to ca.value_ :) //ca.value() = 2;
// compiler error! :) cout <<
f <<
" " <<
g <<
" " <<
h <<
endl;
// 1 1 1
struct A {   const int&
value(const std::size_t i) const { return values_[i];
}
int&
value(const std::size_t i)
{ return values_[i];
} private: std::vector<int>
values_;
// Storing the values in a vector/list/etc is an implementation detail. // - I can validate the index, but not the value :( // - I can change the type of values, without affecting clients :) };
struct A {   int  value() const { return value_;
} // getter void value(int v) { value_=v;
}
// setter private: int value_;
};
vector<int>::iterator
A::value_begin()
{return values_.begin();} vector<int>::const_iterator A::value_begin()const{return values_.begin();} ... a.value_begin()[252]=3;
int b=a.value_begin()[4];
vector<int>
c(a.value_begin(),a.value_end())
struct A {   // getter   int&
getValue() const { return value_;
} // setter void setValue(const int&
value) {
// validate value here
value_ = value;
} private:
int value_;
};