30

我是 C++ 新手,我有一些关于代码分离的一般性问题。我目前在一个文件中构建了一个小型应用程序。我现在要做的是将其转换为单独的文件,以便它们包含类似的代码或诸如此类的东西。我现在真正的问题是,我怎么知道如何分开事物?代码应该分开的不可见边距是多少?

另外,头文件有什么用?是否转发声明方法和类,以便我可以在编译期间将它们包含在链接器中之前在我的代码中使用它们?

任何对方法或最佳实践的见解都会很棒,谢谢!

4

4 回答 4

28

头文件应包含类和函数声明。

源文件包含类和函数定义。

每个头文件有一个声明,每个源文件有一个定义是标准做法(即阅读更容易),尽管对于小型(阅读更简单的帮助程序)对象,您有时会将它们与相关的更重要的对象组合在一起。

示例:班级菜单

Menu.h:     Contains the Menu declaration.
Menu.cpp:   Contains the Menu definition.

头文件包含声明的原因是您可以从多个源文件中包含它们,因此每个源文件对每个类和函数都有完全相同的定义。

这样考虑:
如果您没有头文件,那么您需要在每个源文件中都有类和/或函数声明(没有)定义,这意味着每个文件中都有相同声明的副本。因此,如果您修改一个类,您需要在每个文件中进行相同的修改。通过使用头文件,您可以将声明放在一个位置,因此只需修改一个对象。

于 2008-11-11T04:56:38.890 回答
23

First, you should not put anything into headers that is not needed to be visible by any other file, other than the one that needs it. Then, let's define something we need below.

Translation Unit

A Translation Unit is the current code being compiled, and all the code included by it, directly or indirectly. One Translation unit translates to one .o / .obj file.

Program

That's all your .o / .obj files linked together into one binary file that can be executed to form a process.

What are the main points of having different translation units?

  1. Reduce dependencies, so that if you change one method of one class, you don't have to recompile all the code of your program, but only the affected translation unit. An
  2. Reduce possible name clashes by having translation unit local names, that are not visible by other translation unit when linking them together.

Now, how can you split your code into different translation units? The answer is there is no "so you do it!", but you have to consider it on a case-by-case basis. It's often clear, since you have different classes, which can and should be put in different translation units:

foo.hpp:

/* Only declaration of class foo we define below. Note that a declaration
 * is not a definition. But a definition is always also a declaration */
class foo;

/* definition of a class foo. the same class definition can appear 
   in multiple translation units provided that each definition is the same  
   basicially, but only once per translation unit. This too is called the  
   "One Definition Rule" (ODR). */
class foo {
    /* declaration of a member function doit */
    void doit();

    /* definition of an data-member age */
    int age;
};

Declare some free functions and objects:

/* if you have translation unit non-local (with so-called extern linkage)  
   names, you declare them here, so other translation units can include  
   your file "foo.hpp" and use them. */
void getTheAnswer();

/* to avoid that the following is a definition of a object, you put "extern"  
   in front of it. */
extern int answerCheat;

foo.cpp:

/* include the header of it */
#include "foo.hpp"

/* definition of the member function doit */
void foo::doit() {
    /* ... */
}

/* definition of a translation unit local name. preferred way in c++. */
namespace {
    void help() {
        /* ... */
    }
}

void getTheAnswer() {
    /* let's call our helper function */
    help();
    /* ... */
}

/* define answerCheat. non-const objects are translation unit nonlocal  
   by default */
int answerCheat = 42;

bar.hpp:

/* so, this is the same as above, just with other classes/files... */
class bar {
public:
    bar(); /* constructor */
}; 

bar.cpp:

/* we need the foo.hpp file, which declares getTheAnswer() */
#include "foo.hpp"
#include "bar.hpp"

bar::bar() {
    /* make use of getTheAnswer() */
    getTheAnswer();
}

Please note that names within an anonymous namespace (as above) do not clash since they appear to be translation unit local. in reality they are not, they just have unique names so that they do not clash. if you really want (there is little reason to) translation unit local names (for example because of compatibility with c so C code can call your function) you can do it like this:

static void help() { 
    /* .... */
}

The ODR also says that you cannot have more than one definition of any object or non-inline function in one program (classes are types, not objects, so it doesn't apply to them). So you have to watch out not to put non-inline functions into headers, or not putting objects like "int foo;" in headers. That will cause linker errors then when the linker tries to link the translation units including those headers together.

I hope i could help you a bit. Now that was a long answer, there are indeed errors somewhere. I know that a translation unit strictly is defined another way (output of the pre-processor). But i think it would not add big value to include that into the above, and it would confuse the matter. Please feel free to slap me if you find real bugs :)

于 2008-11-11T05:27:04.663 回答
4

决定如何将代码分成不同的类/函数是编程的主要任务之一。关于如何做到这一点有许多不同的指导方针,我建议您阅读一些关于 C++ 和面向对象设计的教程来帮助您入门。

一些基本的指导方针将是

  • 把一起使用的东西放在一起
  • 为域对象创建类(例如文件、集合等)

头文件允许您声明一个类或函数,然后在几个不同的源文件中使用它。例如,如果您在头文件中声明一个类

// A.h
class A
{
public:
    int fn();
};

然后,您可以在多个源文件中使用此类:

// A.cpp
#include "A.h"
int A::fn() {/* implementation of fn */}

//B.cpp
#include "A.h"
void OtherFunction() {
    A a;
    a.fn();
}

因此,头文件使您能够将声明与实现分开。如果您要将所有内容(声明和实现)放在源文件(例如 A.cpp)中,则尝试将其包含在第二个文件中,例如

// B.cpp
#include  "A.cpp" //DON'T do this!

然后你可以编译 B.cpp 但是当你尝试链接你的程序时,链接器会抱怨你有多个定义的对象 - 这是因为你有多个 A 的实现副本。

于 2008-11-11T05:01:00.973 回答
0

建议: 1. 现在为您的应用准备好设计。2. 根据设计,创建相互交互的必要对象。3.重构或完全改变现有代码以适应新创建的设计。

头文件为可能使用其功能的其他类提供接口。

于 2009-02-18T16:27:38.730 回答