考虑以下类:
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是否危险。
正如您所提到的,getter 和 setter 的主要目的是提供一种统一的方式来访问和设置私有实例变量。
从您的包装器解决方案中,如果在程序生命周期的某个时候您决定更改 setter,您可以扔掉包装器并将其替换为原始的 getter/setter,就像在 MyClass1 中一样 - 而不必更改调用它的代码的所有其他组件。
因此,从这一点来看,我认为您的解决方案是节省您多输入几行代码的有效方法。
我在那里没有看到特别危险的东西,但它似乎没有任何收获。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 ) {}
};
https://ideone.com/qqgj3D
它本身并不危险,只是更难使用(对于类用户)。 MyClass2 混淆了界面并使其混乱。
它可能会使您编写代码变得不那么乏味,但您只需要编写一次代码 - 该接口将被多次重用。 因此,应该青睐代码阅读器。
因此,MyClass1优于MyClass2和Potatoswatter解决方案。
(即使C++有办法做到这一点:
class MyClass3
{
double x, y, z;
}
MyClass3::z::operator=(double) { ... } // property set
double MyClass3::z::operator() { ... } // property get
就像在 C# 中一样,我认为这仍然是一个坏主意,因为这种"隐藏"代码会导致错误的假设,从而减慢调试速度。 函数样式至少让你认为可能有一些比原始副本更复杂的事情发生。