我应该在哪里实现我的类方法?

Posted

技术标签:

【中文标题】我应该在哪里实现我的类方法?【英文标题】:Where should I implement my class method? 【发布时间】:2015-02-02 17:45:20 【问题描述】:

我知道我的类方法有三种不同的实现“位置”:

1) 在我的类(.h 文件)中定义方法并在我的 .cpp 文件中实现它

//.h
class Foo

  int getVal() const;
;


//.cpp
int Foo::getVal() const
 return 0; 

2) 在我的类(.h 文件)中定义并实现方法。

//.h
class Foo

  int getVal() const
   return 0; 
;

3) 在我的类中定义方法并在类之外但在我的头文件中实现它。

//.h
class Foo

  int getVal() const;
;

int Foo::getVal() const
 return 0; 

这三种方法的主要区别是什么?

【问题讨论】:

#3 可能会导致链接器错误(只要将此标头包含在多个源文件中)。 #2 将让编译器在适用的情况下内联函数(不需要整个程序优化),因此它通常用于小型 getter/setter 函数,就像您在示例中显示的那样。对于模板类(仅限),#1 不是一个选项,#2 和 #3 等效。 为了更快的编译速度,尽量选择#1。 相关问题:here和here 请注意,在您说“定义”的地方,正确的术语是声明。函数的定义包括函数体。 【参考方案1】:

这个问题包含三个要素:可读性(代码看起来有多好),编译(编译器可以优化多少)和实现隐藏(如果您将代码用作库,您可能不想显式共享你与世界的特殊酱汁)。

方法一只在头文件中暴露函数的接口。这意味着您展示了一个漂亮干净的界面,并且您的实现不会以明文形式公开。但是,代码不能跨编译单元内联,因此它在运行时可能会变慢一些(实际上,这只对非常非常非常小的一部分代码很重要)。这应该是您的默认方式。

方法 2 是隐式内联。长函数会使您的课程变得混乱,这(恕我直言)是不好的。还向世界公开您的实现。但是,该函数可以内联,并且比在其他地方定义它更简洁。我将其保留用于非常小的功能。

方法3实际上是非法的,因为你会打破单一定义的规则,但以下是可以的:

//Foo.h
class Foo 
  int getVal() const;
;

inline int Foo::getVal() const  
  return 0; 

当我想保持类定义干净但希望头文件中的函数定义(用于内联或模板函数)时,我会使用它。

【讨论】:

几乎任何现代链接器都可以跨编译单元内联。 另外,模板改变了一切,值得一提 @Mooing Duck,是的,但是您需要在内联后运行其他优化通道以获得最佳效果,而这只能通过链接时间优化来完成。 @sbabbi:这就是我说链接器这样做的原因。这篇文章说“代码不能跨编译单元内联”,这是不正确的。【参考方案2】:

(1) 对于大型项目,编译速度会更快(只需在 Foo.cpp 中编译一次getVal 的定义,如果定义发生变化只需重新编译一件事),并且您将获得一个非常清晰的界面为想要查找它的人准备的课程。另一方面,您不能内联getVal()

(2) 和 (3) 将编译较慢,并为您的定义更改添加更多依赖项。但是你可以内联getVal()。如果getVal 是模板函数,这也是必需的。 注意 (3) 如果您的标头被多次包含,将导致链接器错误 - 您必须标记记住标记您的函数inline。这是选择 (1) 和 (2) 而非 (3) 的一个很好的理由。

实际上,这不是选择 (1) 与 (2) 的问题。您可能会在大型项目中同时使用这两种方法——将应该内联的函数(和模板)的定义放在标题中,并将内联对 cpp 没有意义的函数放在 cpp 中。

【讨论】:

这个答案不完整。它只描述编译速度,这不是唯一的问题,也可能不是最重要的问题。 @anatolyg 对于我需要几分钟才能构建的项目,这是最重要的问题。 答案中唯一缺少的是如果标题多次包含在 3) 中,可能会出现 ODR 问题;不同意@anatolyg 的评论并已赞成 @anatolyg 答案并不“仅”描述编译速度。哪个是“最重要”的问题纯粹是一个意见问题,也是所讨论项目的实际功能。【参考方案3】:

在我开始之前的一个小提示,有很多非常相似的词用来描述这些东西,我将使用头文件中的部分声明(int getVal() const)和cpp中的部分的实现文件 (int Foo::getVal() const)。如果这些不完全准确,请致歉。

另请注意,如果不清楚,好处是对其他人的惩罚。

1) 在我的类(.h 文件)中定义方法并在我的 .cpp 文件中实现它

这被认为是标准方法,通常被认为是默认方法(有很多例外,所以默认可能有点强)。

它将声明与实现分开。这提供了一些好处:

您只需要编译一次实现。这可能会节省编译时间。 您只需要引用声明即可。这可以避免排序问题,并且对于类之间的循环引用至关重要。 您不分发您的实现,这对于封闭源系统很有用,因为您经常需要分发大量 .h 文件以供其他人插入您的系统。

2) 在我的类(.h 文件)中定义并实现方法。

这称为内联实现,它应该只用于简单的实现。它也有一些好处:

大多数编译器将此视为一个巨大的内联提示。它不能保证 inling,但很有可能,这对于简单的方法可能是一个胜利。属性样式方法是一个常见示例。 您的实现很容易找到,因为它直接在声明中。

3) 在我的类中定义方法并在类之外但在我的头文件中实现它。

我以前没见过这个,但假设它在定义不重要但你想要 2 的内联好处时使用。IdeaHat 提到在这种情况下需要 inline 关键字。

【讨论】:

#3 对于复杂的模板很常见。你有一个相对正常的类定义,然后是复杂的函数定义。 template 情况下不需要 inline 关键字。

以上是关于我应该在哪里实现我的类方法?的主要内容,如果未能解决你的问题,请参考以下文章

C++的=重载问题,怎样为两个有相同成员的类赋值

我应该在哪里定义 ExtJS 4 MVC 中的全局函数?

Maven - 如何处理生成的类

idea怎么查看jar里的类调用链?

我应该在哪里使用 C# 调用方法 connection.Open()?

我应该在 Angular 的类中将方法写成箭头函数吗