类中派生信息的成员函数

时间:2023-01-15 16:51:32

While designing an interface for a class I normally get caught in two minds whether should I provide member functions which can be calculated / derived by using combinations of other member functions. For example:

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

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;
};

Should I provide this as a class member function or probably a namespace where I can define this method? Which one is preferred?

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

6 个解决方案

#1


In general, you should probably prefer free functions. Think about it from an OOP perspective.

通常,您可能更喜欢*功能。从OOP的角度考虑它。

If the function does not need access to any private members, then why should it be given access to them? That's not good for encapsulation. It means more code that may potentially fail when the internals of the class is modified.

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

It also limits the possible amount of code reuse.

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

If you wrote the function as something like this:

如果你写这个函数是这样的:

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

Then the same implementation of getSelectedDocs will work for any class that exposes the required functions, not just your DocContainer.

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

Of course, if you don't like templates, an interface could be used, and then it'd still work for any class that implemented this interface.

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

On the other hand, if it is a member function, then it'll only work for this particular class (and possibly derived classes).

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

The C++ standard library follows the same approach. Consider std::find, for example, which is made a free function for this precise reason. It doesn't need to know the internals of the class it's searching in. It just needs some implementation that fulfills its requirements. Which means that the same find() implementation can work on any container, in the standard library or elsewhere.

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

Scott Meyers argues for the same thing.

斯科特迈耶斯争辩同样的事情。

If you don't like it cluttering up your main namespace, you can of course put it into a separate namespace with functionality for this particular class.

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

#2


I think its fine to have getSelectedDocs as a member function. It's a perfectly reasonable operation for a DocContainer, so makes sense as a member. Member functions should be there to make the class useful. They don't need to satisfy some sort of minimality requirement.

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

One disadvantage to moving it outside the class is that people will have to look in two places when the try to figure out how to use a DocContainer: they need to look in the class and also in the utility namespace.

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

#3


The STL has basically aimed for small interfaces, so in your case, if and only if getSelectedDocs can be implemented more efficiently than a combination of isDocSelected and getDoc it would be implemented as a member function.

在STL已基本目标为小接口,所以你的情况,当且仅当getSelectedDocs可以更有效地比isDocSelected和的getDoc的组合来实现它会被作为一个成员函数来实现。

This technique may not be applicable anywhere but it's a good rule of thumbs to prevent clutter in interfaces.

这种技术可能不适用于任何地方,但是防止接口混乱是一个很好的规则。

#4


I agree with the answers from Konrad and jalf. Unless there is a significant benefit from having "getSelectedDocs" then it clutters the interface of DocContainer.

我同意康拉德和贾尔夫的答案。除非得到“getSelectedDocs”带来显着的好处,否则它会使DocContainer的界面变得混乱。

Adding this member triggers my smelly code sensor. DocContainer is obviously a container so why not use iterators to scan over individual documents?

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

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

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

Then, use a functor that creates the vector of documents as it needs to:

然后,使用一个函子来创建文档向量,因为它需要:

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));
}

This is a bit more verbose (at least until we have lambdas), but an advantage to this kind of approach is that the specific type of filtering is not coupled with the DocContainer class. In the future, if you need a new list of documents that are "NotSelected" there is no need to change the interface to DocContainer, you just write a new "IsDocNotSelected" class.

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

#5


The answer is proabably "it depends"...

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

If the class is part of a public interface to a library that will be used by many different callers then there's a good argument for providing a multitude of functionality to make it easy to use, including some duplication and/or crossover. However, if the class is only being used by a single upstream caller then it probably doesn't make sense to provide multiple ways to achieve the same thing. Remember that all the code in the interface has to be tested and documented, so there is always a cost to adding that one last bit of functionality.

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

#6


I think this is perfectly valid if the method:

我认为如果方法是完全有效的:

  • fits in the class responsibilities
  • 适合班级职责

  • is not too specific to a small part of the class clients (like at least 20%)
  • 对于一小部分班级客户来说并不太具体(至少20%)

This is especially true if the method contains complex logic/computation that would be more expensive to maintain in many places than only in the class.

如果方法包含复杂的逻辑/计算,在许多地方维护比在类中维护更昂贵,则尤其如此。

#1


In general, you should probably prefer free functions. Think about it from an OOP perspective.

通常,您可能更喜欢*功能。从OOP的角度考虑它。

If the function does not need access to any private members, then why should it be given access to them? That's not good for encapsulation. It means more code that may potentially fail when the internals of the class is modified.

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

It also limits the possible amount of code reuse.

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

If you wrote the function as something like this:

如果你写这个函数是这样的:

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

Then the same implementation of getSelectedDocs will work for any class that exposes the required functions, not just your DocContainer.

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

Of course, if you don't like templates, an interface could be used, and then it'd still work for any class that implemented this interface.

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

On the other hand, if it is a member function, then it'll only work for this particular class (and possibly derived classes).

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

The C++ standard library follows the same approach. Consider std::find, for example, which is made a free function for this precise reason. It doesn't need to know the internals of the class it's searching in. It just needs some implementation that fulfills its requirements. Which means that the same find() implementation can work on any container, in the standard library or elsewhere.

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

Scott Meyers argues for the same thing.

斯科特迈耶斯争辩同样的事情。

If you don't like it cluttering up your main namespace, you can of course put it into a separate namespace with functionality for this particular class.

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

#2


I think its fine to have getSelectedDocs as a member function. It's a perfectly reasonable operation for a DocContainer, so makes sense as a member. Member functions should be there to make the class useful. They don't need to satisfy some sort of minimality requirement.

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

One disadvantage to moving it outside the class is that people will have to look in two places when the try to figure out how to use a DocContainer: they need to look in the class and also in the utility namespace.

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

#3


The STL has basically aimed for small interfaces, so in your case, if and only if getSelectedDocs can be implemented more efficiently than a combination of isDocSelected and getDoc it would be implemented as a member function.

在STL已基本目标为小接口,所以你的情况,当且仅当getSelectedDocs可以更有效地比isDocSelected和的getDoc的组合来实现它会被作为一个成员函数来实现。

This technique may not be applicable anywhere but it's a good rule of thumbs to prevent clutter in interfaces.

这种技术可能不适用于任何地方,但是防止接口混乱是一个很好的规则。

#4


I agree with the answers from Konrad and jalf. Unless there is a significant benefit from having "getSelectedDocs" then it clutters the interface of DocContainer.

我同意康拉德和贾尔夫的答案。除非得到“getSelectedDocs”带来显着的好处,否则它会使DocContainer的界面变得混乱。

Adding this member triggers my smelly code sensor. DocContainer is obviously a container so why not use iterators to scan over individual documents?

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

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

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

Then, use a functor that creates the vector of documents as it needs to:

然后,使用一个函子来创建文档向量,因为它需要:

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));
}

This is a bit more verbose (at least until we have lambdas), but an advantage to this kind of approach is that the specific type of filtering is not coupled with the DocContainer class. In the future, if you need a new list of documents that are "NotSelected" there is no need to change the interface to DocContainer, you just write a new "IsDocNotSelected" class.

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

#5


The answer is proabably "it depends"...

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

If the class is part of a public interface to a library that will be used by many different callers then there's a good argument for providing a multitude of functionality to make it easy to use, including some duplication and/or crossover. However, if the class is only being used by a single upstream caller then it probably doesn't make sense to provide multiple ways to achieve the same thing. Remember that all the code in the interface has to be tested and documented, so there is always a cost to adding that one last bit of functionality.

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

#6


I think this is perfectly valid if the method:

我认为如果方法是完全有效的:

  • fits in the class responsibilities
  • 适合班级职责

  • is not too specific to a small part of the class clients (like at least 20%)
  • 对于一小部分班级客户来说并不太具体(至少20%)

This is especially true if the method contains complex logic/computation that would be more expensive to maintain in many places than only in the class.

如果方法包含复杂的逻辑/计算,在许多地方维护比在类中维护更昂贵,则尤其如此。