class vs helper例程 - C ++

时间:2012-02-16 10:29:16

标签: c++ c oop

我是C ++的新手,来自C背景。

我目前正在开发一个C ++项目,当我添加代码时,我经常发现自己要求我应该有一组帮助程序或为它们创建专用类。

对此有何建议?我可以看看是否有代码恢复元素,或者commonlaties然后创建一个类是有道理的。但是,如果一组辅助例程中的代码仅用于单个功能的专用任务,那么将它们放入类中会获得什么?

我意识到我的问题非常抽象,可能含糊不清,但任何建议/最佳实践都会受到赞赏。

感谢。

8 个答案:

答案 0 :(得分:10)

在进行面向对象编程时使用类,即在操作某种 object 类型时,而不是在编写实用函数时。

更具体地说,在C中你有一个

typedef struct { /*...*/ } Foo;

有各种各样的

Foo *make_foo();
void print_foo(FILE *, Foo const *);
// etc.

您应该将Foo对象上运行的函数放在class Foo中。但是当你实现了一堆有用的数学运算时,它们只接受并返回浮点数,无论如何都要使它们成为独立函数,并考虑使用命名空间而不是类来将它们组合在一起。

C ++的优点在于它不会强迫基于类的方法like Java does;它不是每个问题领域的良好匹配。

答案 1 :(得分:8)

这取决于功能。如果他们操纵一组共同的数据, 将数据放在一个类中,并使它们成为成员是有意义的。如果 他们或多或少地彼此独立,把他们放在一个班级里 实际上是误导;它暗示了一种不存在的关系。

无论如何,当他们更多时,不要害怕使用免费功能 适当。

答案 2 :(得分:2)

在我看来,在优秀的OO设计中你不应该做任何帮助类。每个类应该代表现实世界中的某个对象,并且应该包含表示此对象所执行操作的方法。在现实世界中,我们没有任何与某个对象无关的行为。所以我相信辅助类或静态方法在OO开发中是一个地狱:)。应该提到的是,如果你创建的类不代表你系统的任何对象而且它仅用于内部使用(只在你的方法中使用而不会在uml中显示),它可以被移动到另一个子模块项目。

答案 3 :(得分:1)

以下是使课程有用的一些原因:

  • 您可以通过类名避免名称冲突。

  • 类名可能会简化函数名称和/或使它们更具可读性。

  • 如果您的功能需要枚举,那么这些枚举将与您的功能整齐地打包

  • 如果你的函数访问常量,常量将被打包到类中。

  • 如果您运行支持设置(或将来的版本),将它们作为非静态方法实现,可以使多线程更容易。

答案 4 :(得分:1)

如果函数没有内部状态并且是可重入的,我看不到定义具有静态成员的类或者只是为了调用方法而创建类的对象 - 这将是毫无意义的类型。

答案 5 :(得分:1)

  

什么是class

在最严格的OOP意义上,类是一组相关数据,其中包含对这些数据进行操作的方法。然而,C ++远非纯粹的OOP语言,而封装是一个更好的目标,一些宗教过于热心的无知乌托。

  

哪些数据应放入class

相关数据通常应该绑在一起,例如,缓冲区的长度和相关的缓冲区属于一起,因为你不能使用没有长度的缓冲区。

Encapsulation(和Demeter法则)然后建议它们应该是private,这样该类的客户端不依赖于它的内部表示。但在实践中,将一些信息直接暴露给公众是可行的,以避免产生一堆getter / setter函数(尽管这些函数都有自己的优点)。

至少,与不变量(*)相关联的数据应为private,以便可以强制执行这些不变量

  

应将哪些功能放入class

任何需要直接访问类私有数据的函数都应该是一个类方法。一个值得注意的例外是运营商,其签名是固定的。这可以通过为他们提供友好访问来解决。

证据是,任何不需要直接访问的函数(因为其他方法为其功能提供了足够的信息)都可以更好地实现为自由函数。这个增加了封装

典型的例子是std::string::find_first_of(和相关的)应该作为自由函数实现。

  

什么是 blob

有时您会发现自己拥有大量相关数据,但它们之间没有强大的不变性,例如:

struct State {
  Language language;
  Page currentPage;
};

如果更改语言,则不会更改页面,反之亦然。为方便起见,这两者仅仅是在一起。这不是class,因为它的结构非结构化,有时也被称为blob。这里不需要更多的封装,因此方法是多余的(虽然构造函数可以提供帮助)。


(*)在不变量

如上所述,缓冲区的长度与缓冲区本身紧密相关,它们应该一起更改。因此,应该存在一个具有两个成员的缓冲类:长度和缓冲区,并管理这两个成员,以便不变 length的长度为buffer总是持有(就外部观察者而言)。

另一方面,如果您创建std::string filename;属性并将其隐藏在BigObject旁边,logmessageanotherthingy旁边,那么您做错了。 BigObject应该有一个Filename filename;属性,并让它到Filename类以保持这个不变量。

答案 6 :(得分:1)

C ++允许您比C更直接地表达几种不同类型的封装和抽象,但没有硬性规则。

不变量

如果你当前有一个结构(甚至你通常会传递一些原语),但是你想要强制执行一些使你的代码更易于推理的不变量,那么使用具有私有数据成员的类就很有用了。可以维护那些不变量的访问器。

明显的例子是std::string{ char const *p; int length; }。使用第二种形式,您必须手动处理(de)分配,确定p是否合法,并确保在任何地方检查,如果需要,手动强制执行nul-termination等。

接口和继承

你可以在C:interface~ =包含函数指针的结构中实现这些,继承〜=使“base”结构成为“派生”结构的第一个成员,并在它们之间快速地进行转换。实际上,这些机制在C框架中被广泛使用。

但是,如果你想要运行时多态性,C ++中编译器支持的继承和虚方法调度通常会更清晰,更清晰。


除了这些,有很多情况可以用类来表达,但不一定是。

相互关联的函数和结构的集合可以放在一个类中,但只是将它们分组到命名空间中并将数据保存在可公开访问的struct中也可以。 。实际上,即使数据具有不变量,最小化特权方法的数量并将其他相关逻辑放在自由函数中也是更好的样式(它减少了不变量需要检查或保证的位置数)。

某些类型的变体通过编译时比运行时多态(即模板)更好地表达。在这种情况下,您不需要继承关系,因为模板可以使用duck-typing。

使用opmpl指针typedef就像使用pimpl或handle / body类一样容易实现依赖性隐藏,例外情况是公共外部类需要做更多事情来管理impl指针的生命周期。由shared_ptr或unique_ptr提供。

当然,有些代码自然是功能性的或程序性的,将它们编成课堂也没有任何好处。

答案 7 :(得分:-2)

我的状况相同。我刚从C世界来,现在我一直在使用C ++。

我的建议是创建一个类并调用它的方法。

也许在未来,有人可以使用同一个类;)