对POD类中的属性使用带有隐式getter和setter的模板类对象是很好的做法吗?
考虑以下模板示例:
template<typename T>
class Attribute
{
protected:
T m_val;
public:
T * getAddress() { return &m_val; }
T get() { return m_val; }
void set(T v) { m_val = v; }
};以及它的用法:
class A
{
public:
Attribute<float> floatAttr;
Attribute<int> intAttr;
Attribute<long> longAttr;
};有了这个,就有可能封装数据,但实现开销更小。
这是不好的还是好的做法(为什么)?
编辑:说明我在这里看到的优点。不需要手工实现每个getter setter函数,但这些函数仍然有一些通常的优点:
发布于 2017-03-28 12:21:21
在其他一些语言中,getter和setter是一种防止实现细节转义到接口中的方法;一旦直接公开字段,您可能无法在不更改访问字段的代码中的所有站点的情况下,稍后重新实现为属性(带有getter和setter函数)。
在C++中,这并不适用(如此强烈)。您可以将任何字段的类型更改为覆盖operator=并隐式转换为所需类型的类(对于"get“一方)。(当然,也有一些不适用的用法;例如,如果在客户端代码中创建了指向字段的指针或引用--尽管我个人会避免这样做,并认为这是一种可疑的做法)。
此外,由于C++是静态类型的,如果您需要将字段和相应的访问更改为具有适当调用的getter/setter对,那么工具(IDE等)也更容易提供自动重构。
为了证明这一点,下面是对Attribute模板的修改,它允许您的“属性”充当一个直接暴露的字段(除了&将返回属性的地址而不是隐藏字段):
template<typename T>
class Attribute
{
protected:
T m_val;
public:
operator T() { return m_val; }
T &operator=(const T &a) { m_val = a; return m_val; }
};如果你真的愿意,你也可以覆盖operator&
T *operator&() { return &m_val; }..。然而,这样做很大程度上破坏了封装(为此,您可能考虑将operator=的返回类型更改为T或void,原因相同)。
如果您最初直接公开了该字段,则可以将其定义替换为上述模板的实例,并且大多数使用都不会受到影响。这说明了为什么getter/setter模式在C++中并不总是必要的原因之一。
您自己的解决方案虽然封装了getter/setter函数后面的原始字段,但实际上还公开了另一个字段:Attribute<T>成员(在示例中为floatAttr等)。要使它作为封装的一种手段,您依赖于用户不知道(或关心)属性字段本身的类型;也就是说,您期望没有人知道(或关心):
A a;
Attribute<float> & float_attr = a.floatAttr;当然,如果他们不这样做并以您想要的方式访问字段,那么以后可以通过更改“属性”字段的类型来更改实现:
A a;
float f = a.floatAttr.get();..。因此,在这个意义上,您实现了一些封装;真正的问题是,有更好的方法来实现它。:)
最后,值得一提的是,您建议的Attribute模板以及我前面介绍的替代方法都将字段移动到一个类(Attribute<T>表示某些T)中,这个类与原始父类(A)是分开的。如果要更改实现,则该实现现在在某种程度上受到这一事实的限制;属性对象自然不具有对包含它的对象的引用。例如,假设我有一个类B,它有一个属性level
class B {
public:
Attribute<int> level;
};现在,假设我稍后添加了一个"miminum“字段,min_level
class B {
public:
Attribute<int> level;
Attribute<int> min_level;
};此外,假设我现在希望在赋值时将level约束为min_level的值。这不是直截了当!虽然我可以为level提供一个具有自定义实现的新类型,但它将无法从包含的对象访问min_level值:
class LevelAttribute {
int m_val;
public:
T &operator=(const T &a) {
m_val = std::max(min_level, a); // error - min_level not defined
}
}要使其工作,您需要将包含的对象传递到LevelAttribute对象,这意味着存储一个额外的指针。典型的、老式的setter (直接在包含字段的类中声明为函数)避免了这个问题。
https://stackoverflow.com/questions/43069213
复制相似问题