我应该在哪里实现我的类方法?
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 关键字。以上是关于我应该在哪里实现我的类方法?的主要内容,如果未能解决你的问题,请参考以下文章