考虑以下课程:
class MyClass1
{
public:
double x() const {return _x;} // getter
double y() const {return _y;} // getter
double z() const {return _x*_y;} // getter
void x(const double var) {_x = var;} // setter
void y(const double var) {_y = var;} // setter
void z(const double var) {_x = var; _y = 1;} // setter
protected:
double _x;
double _y;
};
由于MyClass1
的实际内容是一个实现细节,getter和setter提供了一种统一的方法来获取和设置类内容,即使它们是相互依赖的(此处_z
内部不存在但是对于用户,z
是一个变量,如x
和y
)。
现在为了避免必须为x
和y
编写getter / setter,可以使用这样的包装器:
template <typename Type>
class Wrapper
{
public:
constexpr Wrapper(const Type& value) {_value = value;}
constexpr Type& operator()() {return _value;}
constexpr const Type& operator()() const {return _value;}
constexpr void operator()(const Type& value) {_value = value;}
protected:
_value;
};
现在原来的班级成了:
class MyClass2
{
public:
Wrapper<double> x;
Wrapper<double> y;
double z() const {return x*y;} // getter
void z(const double var) {x = var; y = 1;} // setter
};
避免必须编写getter / setter是危险的做法还是一个好的解决方案?
注意:此处MyClass1
和MyClass2
只是示例。我的问题非常“一般”:当getter / setter返回/设置内部值时,用提议的Wrapper
替换类的getter / setter是危险的。
答案 0 :(得分:1)
正如您所提到的,getter和setter的主要目的是提供访问和设置私有实例变量的统一方法。
从你的包装器解决方案中,如果在程序生命周期的某个时间你决定更改一个setter,你可以扔掉包装器并用MyClass1中的原始getter / setter替换它 - 而不必改变所有其他调用它的代码的组件。
从那时起,我认为您的解决方案是一种有效的方法,可以避免输入额外的几行代码。
答案 1 :(得分:1)
我认为没有什么特别危险,但它似乎没有任何收获。 C ++为任何类型的对象提供引用语义,这与setter函数不兼容。有时,实际内容不仅仅是一个细节。
你也可以走另一条路(事实上,这是我在点击这个问题时所期望的):
struct virt_z {
double x;
double y;
struct z_wrap {
virt_z &parent;
operator double() { return parent.x * parent.y; }
z_wrap &operator=( double in ) {
parent.x = in;
parent.y = 1;
return *this;
}
z_wrap( virt_z &in ) : parent( in ) {}
} z;
virt_z() : z( *this ) {}
virt_z( virt_z const &o ) : x( o.x ), y( o.y ), z( *this ) {}
};
答案 2 :(得分:1)
这并不危险,使用起来比较困难(对于班级用户)。 MyClass2模糊了界面并使其混乱。
这可能会让您编写代码变得不那么繁琐,但您只需编写一次代码 - 该接口将被重复使用多次。因此,代码阅读器应该受到青睐。
出于这个原因,MyClass1优于MyClass2和Potatoswatter解决方案。
(即使在C ++中有一种方法可以做到这一点:
class MyClass3
{
double x, y, z;
}
MyClass3::z::operator=(double) { ... } // property set
double MyClass3::z::operator() { ... } // property get
像C#一样,我认为这仍然是一个坏主意,因为这种“隐藏”的代码会导致错误的假设,从而减慢调试速度。函数样式至少让你觉得可能会有比原始副本更复杂的东西。)