类中派生信息的成员函数

时间:2009-02-02 15:48:06

标签: c++ oop

在设计类的接口时,我通常会有两种想法,我是否应该提供可以通过使用其他成员函数的组合来计算/派生的成员函数。例如:

class DocContainer
{
 public:
   Doc* getDoc(int index) const;
   bool isDocSelected(Doc*) const;
   int getDocCount() const;

  //Should this method be here???
  //This method returns the selected documents in the contrainer (in selectedDocs_out)
  void getSelectedDocs(std::vector<Doc*>& selectedDocs_out) const;
};

我应该将它作为类成员函数提供,还是作为我可以定义此方法的命名空间?哪一个更受欢迎?

6 个答案:

答案 0 :(得分:4)

我认为将getSelectedDocs作为成员函数很好。对于DocContainer来说,这是一个非常合理的操作,因此作为成员是有意义的。成员函数应该在那里使类有用。他们不需要满足某种最低要求。

将它移到课堂外的一个缺点是,当试图弄清楚如何使用DocContainer时,人们将不得不查看两个地方:他们需要查看类和实用程序命名空间。

答案 1 :(得分:4)

一般来说,您可能更喜欢免费功能。从OOP的角度考虑它。

如果该功能不需要访问任何私有成员,那么为什么给予访问它们?这对封装来说并不好。这意味着当修改类的内部时可能会失败的更多代码。

它还限制了可能的代码重用量。

如果您按照以下方式编写函数:

template <typename T>
bool getSelectedDocs(T& container, std::vector<Doc*>&);

然后,getSelectedDocs的相同实现将适用于公开所需函数的任何类,而不仅仅是您的DocContainer。

当然,如果你不喜欢模板,可以使用一个接口,然后它仍适用于任何实现这个接口的类。

另一方面,如果它是一个成员函数,那么它只适用于这个特定的类(以及可能的派生类)。

C ++标准库遵循相同的方法。例如,考虑std::find,出于这个确切的原因,它是一个自由函数。它不需要知道它正在搜索的类的内部。它只需要满足其要求的一些实现。这意味着相同的find()实现可以在任何容器,标准库或其他地方使用。

Scott Meyers争辩same thing

如果你不喜欢它混乱你的主命名空间,你当然可以把它放到一个单独的命名空间中,它具有这个特定类的功能。

答案 2 :(得分:3)

STL基本上针对小型接口,因此在您的情况下,当且仅当getSelectedDocs可以比isDocSelectedgetDoc的组合更有效地实施时,它将被实现作为会员职能。

此技术可能无法在任何地方应用,但防止接口混乱是一个很好的规则。

答案 3 :(得分:2)

我同意Konradjalf的答案。除非“getSelectedDocs”有显着的好处,否则它会使DocContainer的界面变得混乱。

添加此成员会触发我的smelly code传感器。 DocContainer显然是一个容器,为什么不使用迭代器来扫描单个文档?

class DocContainer
{
public:
  iterator begin ();
  iterator end ();

  // ...
  bool isDocSelected (Doc *) const;
};

然后,使用一个仿函数来创建文档向量:

typedef std::vector <Doc*> DocVector;
class IsDocSelected {
public:
  IsDocSelected (DocContainer const & docs, DocVector & results)
  : docs (docs)
  , results (results)
  {}

  void operator()(Doc & doc) const
  {
    if (docs.isDocSelected (&doc))
    {
      results.push_back (&doc);
    }
  }
private:
  DocContainer const & docs;
  DocVector & results;
};


void foo (DocContainer & docs)
{
  DocVector results;
  std :: for_each (docs.begin ()
    , docs.end ()
    , IsDocSelected (docs, results));
}

这有点冗长(至少在我们有lambdas之前),但这种方法的一个优点是特定类型的过滤不与DocContainer类耦合。将来,如果您需要一个新的“NotSelected”文档列表,则无需将界面更改为DocContainer,您只需编写一个新的“IsDocNotSelected”类。

答案 4 :(得分:0)

答案可能是“它取决于”......

如果该类是许多不同调用者将使用的库的公共接口的一部分,那么提供大量功能以使其易于使用(包括一些复制和/或交叉)是一个很好的论据。但是,如果该类仅由单个上游调用者使用,则提供多种方法来实现相同的操作可能没有意义。请记住,界面中的所有代码都必须经过测试和记录,因此添加最后一点功能总是有成本。

答案 5 :(得分:0)

我认为如果方法:

,这是完全有效的
  • 符合班级职责
  • 对于一小部分班级客户来说并不太具体(至少20%)

如果该方法包含复杂的逻辑/计算,在许多地方维护而不仅仅是在类中,则尤其如此。