在C ++中组织接口和实现的方法


12

我已经看到,在C ++中,关于头文件中的内容和cpp文件中的内容有几种不同的范例。AFAIK,大多数人,尤其是具有C背景的人会:

foo.h

 class foo {
 private:
     int mem;
     int bar();
 public:
     foo();
     foo(const foo&);
     foo& operator=(foo);
     ~foo();
 }

foo.cpp

 #include foo.h
 foo::bar() { return mem; }
 foo::foo() { mem = 42; }
 foo::foo(const foo& f) { mem = f.mem; }
 foo::operator=(foo f) { mem = f.mem; }
 foo::~foo() {}
 int main(int argc, char *argv[]) { foo f; }

但是,我的讲师通常会像这样向初学者教授C ++:

foo.h

 class foo {
 private:
     int mem;
     int bar() { return mem; }
 public:
     foo() { mem = 42; }
     foo(const foo& f) { mem = f.mem; }
     foo& operator=(foo f) { mem = f.mem; }
     ~foo() {}
 }

foo.cpp

 #include foo.h
 int main(int argc, char* argv[]) { foo f; }
 // other global helper functions, DLL exports, and whatnot

最初来自Java,出于种种原因,我也始终坚持使用第二种方法,例如,只要接口或方法名称更改,我只需要在一个地方更改某些内容,当我喜欢类中事物的不同缩进时,我就喜欢看看他们的执行,而且我发现名字更易读的foo比较foo::foo

我想以任何一种方式收集赞成和反对。也许还有其他方法?

我的方式的一个缺点当然是偶尔需要向前声明。


2
foo.cpp现在与您的foo课程无关,应该将其留空(也许只是#include为了使您的构建系统满意)。
本杰明·班尼尔

2
你的老师疯了。
Lightness Races in Orbit

Answers:


16

尽管第二个版本更易于编写,但它却将接口与实现混合在一起。

每次更改头文件时,都需要重新编译包含头文件的源文件。在第一个版本中,仅在需要更改接口时才更改头文件。在第二个版本中,如果需要更改接口或实现,则可以更改头文件。

除此之外,您不应该公开实现细节,第二版将不必要地重新编译


1
+1我的探查器无法检测放置在头文件中的代码-这也是一个很重要的原因。
尤金

如果你看到我的回答这个问题programmers.stackexchange.com/questions/4573/...你将看到如何在很大程度上取决于类,即语义会怎样使用它(尤其是如果它的暴露部分您的用户界面以及系统中有多少其他类直接使用它)。
CashCow 2011年

3

我是在93-95年第二次这样做的。花了几分钟时间重新编译一个具有5-10个功能/文件的小应用程序(在同一台486 PC上..不,我也不了解类,我才14-15岁,没有互联网) 。

因此,您教初学者和专业人士使用的技术相差很大,尤其是在C ++中。

我认为C ++和F1汽车之间的比较是恰当的。您不要将初学者放在F1汽车中(除非将引擎预热至80-95摄氏度,否则它甚至不会启动)。

不要将C ++作为第一语言来教。您应该有足够的经验,以了解为什么选项2总体上比选项1差,了解一点静态编译/链接的含义,从而了解为什么C ++首选这种方式。


如果你阐述了静态编译有点这个答案甚至会更好/联(我不知道它回来了!)
费利克斯Dombek

2

第二种方法是我称之为完全内联的类。您正在编写一个类定义,但是所有使用它的代码都将内联代码。

是的,编译器决定何时内联,何时不内联...在这种情况下,您虽然可以帮助编译器做出决定,但实际上可能最终生成更少的代码,并且可能更快。

如果您修改了函数的实现,则需要重建所有使用该函数的源,这一优势可能会超过这一事实。在类的轻量级性质中,您将不会修改实现。如果添加新方法,则无论如何都必须修改标题。

但是,随着您的班级变得越来越复杂,甚至添加一个循环,以这种方式进行操作的好处也会下降。

它仍然具有其优势,尤其是:

  • 如果这是“通用功能”代码,则只需包含标头并在多个项目中使用它,而不必链接到包含其源代码的库。

当意味着必须将实现细节引入标头时,内联的不利之处就成为一个问题,例如,您必须开始包括额外的标头。

请注意,模板是一种特殊情况,因为您几乎必须包括实现细节。您可能会将其隐藏在另一个文件中,但是它必须存在。(该规则带有实例化是一个例外,但是通常您内联模板)。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.