用于自动getter-setter方法的C ++类模板 - 好/坏的做法?

时间:2017-03-28 12:10:46

标签: c++ templates getter-setter

在(几乎)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函数,但这些函数仍然具有以下优点:

  • 数据被封装,客户端需要使用getter setter函数,以后仍然可以以不同的方式实现。
  • 内部使用情况仍然隐藏,可以更改。
  • Getter和Setter函数可以作为lambda函数传递。

1 个答案:

答案 0 :(得分:2)

在其他一些语言中,getter和setter是一种阻止实现细节转入接口的方法;直接公开字段后,您可能无法在以后重新实现属性(使用getter和setter函数),而无需更改访问该字段的代码中的所有站点。

在C ++中,这并没有(如此强烈地)适用。您可以将任何字段的类型更改为覆盖operator=的类,并隐式转换为所需类型(对于&#34; get&#34;侧)。 (当然有一些用途并不适用;例如,如果在客户端代码中创建了对该字段的指针或引用 - 尽管我个人会避免这样做,并认为这是可疑的做法。)

此外,因为C ++是静态类型的,所以如果你需要通过适当的调用将字段和相应的访问更改为getter / setter对,工具(IDE等)也更容易提供自动重构。

就证据而言,此处是对Attribute模板的更改,该模板允许您使用&#34;属性&#34;表现得好像它是一个直接暴露的字段(除了&将返回属性的地址而不是隐藏的字段):

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=的返回类型更改为Tvoid,原因相同。)< / p>

如果您最初直接公开了该字段,则可以将其定义替换为上述模板的实例,并且大多数用途不受影响。这说明了为什么在C ++中并不总是需要getter / setter模式。

您自己的解决方案虽然封装了getter / setter函数背后的原始字段,但实际上还公开了另一个字段:示例中的Attribute<T>成员(floatAttr等)。为了使其成为一种封装方法,您依赖于不了解(或关心)属性字段本身类型的用户;也就是说,你期望没有人这样做:

A a;
Attribute<float> & float_attr = a.floatAttr;

当然,如果他们不这样做并以你想要的方式访问字段,那么以后确实可以通过改变&#34;属性&#34;的类型来改变实现。字段:

A a;
float f = a.floatAttr.get();

...所以从这个意义上说,你实现了一些封装;真正的问题是有更好的方法来做到这一点。 :)

最后,值得一提的是,您提出的Attribute模板以及上面显示的替代方案都会将字段移动到与原始父级分开的类(Attribute<T>中) class(A)。如果要改变实施,现在在某种程度上受到这一事实的限制;属性对象自然不会引用包含它的对象。例如,假设我有一个类B,它有一个属性level

class B {
    public:
    Attribute<int> level;
};

现在假设我后来添加了一个&#34; miminum等级&#34;字段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,直接在持有字段的类中声明为函数,避免了这个问题。