设置器和获取器的模板

9

我不太熟悉模板,但我想知道是否可以将它们用于setter和getter方法。例如,在这种情况下:

double exmlClass::getA(void) const
{
    return a_;
}



void exmlClass::setA(const double& a)
{
    a_ = a;
}



double exmlClass::getB(void) const
{
    return b_;
}

如您所见,这些方法几乎相同,除了它们引用另一个私有变量(a_、b_和c_)。在这种情况下,是否有更优雅的编写函数的方式,或者像上面那样是常见做法?如果通常使用模板,请给出如何在上述代码中使用模板的示例。

我还想问的另一个问题是,getter和setter应该如何正确声明?这是好的编码风格吗?

double getA(void) const;
void setA(const double& a);

double getB(void) const;
void setB(const double& b);

double getC(void) const;
void setC(const double& c);

我的意思是getter方法是否应始终为const,而setter方法则应该将参数作为对象的引用传递,而不是复制它,这样可能会慢一点?


8
如果你的类中有过多的getter和setter方法,以至于这成为一个问题,我会认真质疑它的设计。 - sbi
1
我在某种程度上同意 @sbi 的观点。正如那篇文章所暗示的,如果你仅仅是为了访问私有成员而使用setter和getter,那么这是愚蠢的。然而,该文章没有涉及到一种情况:当你有一个合法的需求需要将私有数据视为半透明或完全不透明时(比如大小端、序列化/反序列化等),特别是当表示的类型包装了多个数据片段时。例如:http://codereview.stackexchange.com/questions/7786/c-like-class-properties - Brian Vandenberg
此外,由于那篇文章的作者没有明确说明(只是在结尾处简要提到),我想强调一下文章中的一个问题:3. 缺少必要的运算符和其他方法会迫使用户程序通过 get 和 set 访问器函数来操作对象。如果您的访问器是运算符,这至少意味着该文章的作者不会[必然]有同样的抱怨。 - Brian Vandenberg
@Brian:嗯,OP有一些愚蠢的getter,并且问“这是好的编码风格吗?”这是一个新手偏离“真理之路(™)”的非常可靠的迹象,而不是某人询问如何通过getter抽象出一些实现细节。 - sbi
6个回答

4

向质疑者们问个好!

Boost.Fusion.Map是你寻找的基础。

namespace result_of = boost::fusion::result_of;

class MyClass
{
public:
  struct AType {};
  struct BType {};
  struct CType {};

  template <typename Type>
  typename result_of::at< DataType, Type >::type &
  access(Type) { return boost::fusion::at<Type>(mData); }

  template <typename Type>
  typename result_of::at< DataType, Type >::type const &
  get(Type) const { return boost::fusion::at<Type>(mData); }

  template <typename Type>
  void set(Type, typename result_of::at< DataType, Type >::type const & v)
  {
    boost::fusion::at<Type>(mData) = v;
  }

private:
  typedef boost::fusion::map <
    std::pair<AType, int>,
    std::pair<BType, std::string>,
    std::pair<CType, float> > DataType;
  DataType mData;
};

2
那么,将其中一种方法专门用于执行不同的操作有多容易呢?(因为如果你不这样做,那么你可能就只需要公共数据成员了。) - aschepler
@Brian Vandenberg:很抱歉你的编辑被拒绝了;我调整了代码。 - Matthieu M.

2

设计程序时,尽量减少对getter和setter的需求。你可以通过宏或实现某种属性语法来创建它们(这是可能的,但总会有一些问题无法解决)。 然而,我认为在需要时编写访问器或使用IDE生成它们是最好且最常见的方法。

至于你的第二个问题,对于对象类型,你应该至少使用它,对于基本数据类型,你不需要。个人而言,如果我需要对象的副本,我也不使用它,但其他人可能会认为明确指定更好。


1
以一种方式设计你的程序,使得getter和setter的需求更少。这与面向对象编程的封装规则相违背。 - peoro
@MatthieuM。然后使用typedef。仅仅因为你可能需要更改底层实现而创建一个不透明类型是一种虚假的懒惰形式:http://c2.com/cgi/wiki?FalseLaziness - Brian Vandenberg
@BrianVandenberg:我不知道你的评论与这个5年前的对话有什么关系;我可能已经忘记了一些概念,所以如果你真的希望得到答案,恐怕我需要你推断一下你在谈论什么。 - Matthieu M.
@MatthieuM。除非我理解你的话有误,否则我认为你最后的陈述意味着“最初使其不透明,因为在未来您可能想要更改底层行为”(例如,将其动态分配)。如果我理解错了,请忽略我所说的。 - Brian Vandenberg
@BrianVandenberg:我在谈论隐藏实现细节(类内部数据的表示方式),因此建议将其设置为不透明,以便能够更改所述数据的表示方式;通常情况下,这不应影响行为或公共API。我不同意这种做法通常是错误的懒惰;另一方面,我只建议在API被公开使用时采用此方法,因为在内部隐藏实现细节并不值得麻烦。 - Matthieu M.
显示剩余8条评论

0

我看不出模板化怎么能直接帮助你压缩getter/setter对,除非你想把成员封装在像this这样的模板访问器类中。另一个可能的方法是使用#define宏来模拟C#属性(如果宏不吓到你或任何阅读你代码的人)。

你的getter返回(const)引用、返回成员指针或复制它们取决于几个因素。你必须考虑复制它们是否昂贵(从性能上考虑),复制它们是否语义上有意义,以及返回的信息是否应该能够超越你获取它的类而存在(如果你返回成员的指针/引用,一旦删除你的对象,它就会被悬挂)。如果你想允许成员为空,我会使用指针而不是引用。

对于getter,我倾向于返回任何但原语的const引用,这些我会进行复制。对于setter,惯例上有一个const引用参数。


更简单的封装:Boost.Fusion.Map。 - Matthieu M.

0

从技术上讲,可以创建一个单一的exmlClass::set<PMF>函数模板,使得exmlClass::set<&exmlClass::a_>是有效的。然而,这样做有什么意义呢?


-1

理论上,你可以:

template<typename A, typename B, typename C>
class Object
{
private:
    A _a;
    B _b;
    C _c;

public:
    Object()
    {
    };  // eo ctor

    // properties

    A getA() const {return(_a);}
    void setA(const A& _val) {_a = _val;}

    // etc for B & C
}; // eo class Object

    // .....

    Object<int, double, char> myObject;

我看到这里有几个问题。首先,getter/setter不应该在向类的用户传达信息方面是“抽象的”。你将如何称呼这些getter/setter?getA()?getAValue()?那是什么意思?

其次,这定义了3个。你的对象需要多少个?1、2、4、9?

第三,getter/setter应该根据其功能适当命名:

getName()
getAddress()
getMovie()

听起来你只是想省些打字的功夫,但我认为这不是复杂化设计的借口。

关于第二点,对于对象返回引用(最好是const),但对于像int、char、bool等简单的内置数据类型则没有必要麻烦。


-1

不,您不能定义任何类型的模板来创建具有无限名称集合(如getAgetB等)的函数或类似函数的东西。

宏可以做到这一点,但这是一个更糟糕的想法。

我通常通过const引用传递/返回类对象,但对于简单的内置类型,如double,则直接传值:

public:
  const ClassType& getObj() const;
  void setObj(const ClassType& obj);
  double getNum() const;
  void setNum(double num);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接