首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用于自动getter-setter方法的C++类模板-好/坏的实践?

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

Stack Overflow用户
提问于 2017-03-28 12:10:46
回答 1查看 7.3K关注 0票数 3

对POD类中的属性使用带有隐式getter和setter的模板类对象是很好的做法吗?

考虑以下模板示例:

代码语言:javascript
复制
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; }
};

以及它的用法:

代码语言:javascript
复制
class A
{
public:
    Attribute<float> floatAttr;
    Attribute<int> intAttr;
    Attribute<long> longAttr;
};

有了这个,就有可能封装数据,但实现开销更小。

这是不好的还是好的做法(为什么)?

编辑:说明我在这里看到的优点。不需要手工实现每个getter setter函数,但这些函数仍然有一些通常的优点:

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

回答 1

Stack Overflow用户

回答已采纳

发布于 2017-03-28 12:21:21

在其他一些语言中,getter和setter是一种防止实现细节转义到接口中的方法;一旦直接公开字段,您可能无法在不更改访问字段的代码中的所有站点的情况下,稍后重新实现为属性(带有getter和setter函数)。

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

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

为了证明这一点,下面是对Attribute模板的修改,它允许您的“属性”充当一个直接暴露的字段(除了&将返回属性的地址而不是隐藏字段):

代码语言:javascript
复制
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&

代码语言:javascript
复制
T *operator&() { return &m_val; }

..。然而,这样做很大程度上破坏了封装(为此,您可能考虑将operator=的返回类型更改为Tvoid,原因相同)。

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

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

代码语言:javascript
复制
A a;
Attribute<float> & float_attr = a.floatAttr;

当然,如果他们不这样做并以您想要的方式访问字段,那么以后可以通过更改“属性”字段的类型来更改实现:

代码语言:javascript
复制
A a;
float f = a.floatAttr.get();

..。因此,在这个意义上,您实现了一些封装;真正的问题是,有更好的方法来实现它。:)

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

代码语言:javascript
复制
class B {
    public:
    Attribute<int> level;
};

现在,假设我稍后添加了一个"miminum“字段,min_level

代码语言:javascript
复制
class B {
    public:
    Attribute<int> level;
    Attribute<int> min_level;
};

此外,假设我现在希望在赋值时将level约束为min_level的值。这不是直截了当!虽然我可以为level提供一个具有自定义实现的新类型,但它将无法从包含的对象访问min_level值:

代码语言:javascript
复制
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 (直接在包含字段的类中声明为函数)避免了这个问题。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/43069213

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档