最近我正在写我的getter和setter(注意:真正的类在getter / setter中做更多的事情):
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
这种方法不允许我:
ca.value() = 2
)。更新:请参阅下面的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 :)
};
现在回答问题:
答案 0 :(得分:5)
这种方法不允许我:
- 在const成员函数中按值返回(因为我希望编译器捕获对const对象的赋值ca.value()= 2)。
我不明白你的意思。如果你的意思是我认为你的意思 - 你会感到惊喜:)只是试着让const成员按价值回归,看看你是否能做ca.value()=2
......
但我的主要问题是,如果你想要某种输入验证,为什么不使用专用的setter和专用的getter
struct A {
int value() const { return value_; } // getter
void value(int v) { value_=v; } // setter
private:
int value_;
};
它甚至会减少输入的数量! (一个'=')当你设置。唯一的缺点是你不能通过引用传递值来修改它。
关于编辑后的第二个示例,使用vector
- 使用getter / setter比原始示例更有意义,因为您想要访问值(允许用户更改值)但不是向量(你不希望用户能够改变向量的大小)。
所以即使在第一个例子中我真的建议将成员公开,但在第二个例子中它显然不是一个选项,如果不需要输入验证,使用这种形式的getter / setter确实是一个不错的选择。
另外,当我有类似你的第二种类(带向量)的类时,我喜欢访问begin
和end
迭代器。这样可以更灵活地使用标准工具使用数据(同时仍然不允许用户更改vector
大小,并允许轻松更改容器类型)
另一个好处是随机访问迭代器有一个operator[]
(像指针一样),所以你可以做到
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())
(虽然它可能很难看,除此之外你仍然想要你的吸气剂/定型者)
答案 1 :(得分:5)
"do I *really* need this?"
。答案 2 :(得分:0)
关于输入验证: 在您的示例中,赋值发生在调用代码中。如果要验证用户输入,则需要将要验证的值传递给struct对象。这意味着您需要使用成员函数(方法)。例如,
struct A {
// getter
int& getValue() const { return value_; }
// setter
void setValue(const int& value) {
// validate value here
value_ = value;
}
private:
int value_;
};
顺便说一句,实现.NET属性的方法是幕后的。