是一个好的做法是把C ++类的定义放到头文件中? [英] Is is a good practice to put the definition of C++ classes into the header file?

查看:179
本文介绍了是一个好的做法是把C ++类的定义放到头文件中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我们在Java,Vala或C#中设计类时,我们将定义和声明放在同一个源文件中。但在C ++中,传统上最好将两个或多个文件中的定义和声明分开。

When we design classes in Java, Vala, or C# we put the definition and declaration in the same source file. But in C++ it is traditionally preferred to separate the definition and declaration in two or more files.

如果我只是使用一个头文件, Java?
是否有性能损失或什么?

What happens if I just use a header file and put everything into it, like Java? Is there a performance penalty or something?

推荐答案

答案取决于你创建什么样的类。

The answer depends on what kind of class you're creating.

C ++的编译模型可以追溯到C的日子,因此将数据从一个源文件导入到另一个源文件的方法比较原始。 #include 指令会将您正在包含的文件的内容复制到源文件中,然后将结果视为您一直写入的文件。您需要注意这一点,因为一个C ++策略称为一个定义规则(ODR),这并不奇怪,每个函数和类应该有至多一个定义。这意味着如果你在某个地方声明一个类,那么该类的所有成员函数应该完全没有定义,或者在一个文件中定义一次。有一些例外(我会在一分钟内得到他们),但现在只是把这个规则看作是一个快速,没有例外的规则。

C++'s compilation model dates back to the days of C, and so its method of importing data from one source file into another is comparatively primitive. The #include directive literally copies the contents of the file you're including into the source file, then treats the result as though it was the file you had written all along. You need to be careful about this because of a C++ policy called the one definition rule (ODR) which states, unsurprisingly, that every function and class should have at most one definition. This means that if you declare a class somewhere, all of that class's member functions should be either not defined at all or defined exactly once in exactly one file. There are some exceptions (I'll get to them in a minute), but for now just treat this rule as if it's a hard-and-fast, no-exceptions rule.

如果你接受一个非模板类,并把类定义和实现放入一个头文件,你可能会遇到麻烦的一个定义规则。特别是,假设我有两个不同的.cpp文件,我编译,两个 #include 你的头包含实现和接口。在这种情况下,如果我尝试链接这两个文件在一起,链接器将发现每个包含该类的成员函数的实现代码的副本。此时,链接器将报告错误,因为您违反了一个定义规则:所有类的成员函数有两个不同的实现。

If you take a non-template class and put both the class definition and the implementation into a header file, you might run into trouble with the one definition rule. In particular, suppose that I have two different .cpp files that I compile, both of which #include your header containing both the implementation and the interface. In this case, if I try linking those two files together, the linker will find that each one contains a copy of the implementation code for the class's member functions. At this point, the linker will report an error because you have violated the one definition rule: there are two different implementations of all the class's member functions.

为了防止这种情况, C ++程序员通常将类分解为包含类声明的头文件以及其成员函数的声明,而不执行那些函数。然后将这些实现放入一个单独的.cpp文件中,该文件可以单独编译和链接。这允许你的代码避免遇到ODR的麻烦。这里是如何。首先,每当你将 #include 类头文件分成多个不同的.cpp文件时,每个文件只会获取成员的声明函数,而不是他们的定义,所以你的类的客户端都不会得到定义。这意味着任何数量的客户端都可以 #include 您的头文件,而不会在链接时遇到麻烦。由于您自己的.cpp文件与实现是包含成员函数的实现的唯一文件,在链接时,您可以合并它与任何数量的其他客户端对象文件没有麻烦。这是你拆分.h和.cpp文件的主要原因。

To prevent this, C++ programmers typically split classes up into a header file which contains the class declaration, along with the declarations of its member functions, without the implementations of those functions. The implementations are then put into a separate .cpp file which can be compiled and linked separately. This allows your code to avoid running into trouble with the ODR. Here's how. First, whenever you #include the class header file into multiple different .cpp files, each of them just gets a copy of the declarations of the member functions, not their definitions, and so none of your class's clients will end up with the definitions. This means that any number of clients can #include your header file without running into trouble at link-time. Since your own .cpp file with the implementation is the sole file that contains the implementations of the member functions, at link time you can merge it with any number of other client object files without a hassle. This is the main reason that you split the .h and .cpp files apart.

当然,ODR有一些例外。第一个提出了模板函数和类。 ODR明确声明您可以对同一模板类或函数具有多个不同的定义,前提是它们都是等效的。这主要是为了更容易编译模板 - 每个C ++文件可以实例化相同的模板,而不会与任何其他文件冲突。出于这个原因,以及一些其他技术原因,类模板往往只有一个.h文件没有匹配的.cpp文件。任何数量的客户端都可以<$ p $ c> #include 无麻烦的文件。

Of course, the ODR has a few exceptions. The first of these comes up with template functions and classes. The ODR explicitly states that you can have multiple different definitions for the same template class or function, provided that they're all equivalent. This is primarily to make it easier to compile templates - each C++ file can instantiate the same template without colliding with any other files. For this reason, and a few other technical reasons, class templates tend to just have a .h file without a matching .cpp file. Any number of clients can #include the file without trouble.

ODR的另一个主要异常包括内联函数。规范特别声明ODR不适用于内联函数,所以如果你有一个头部文件的类成员函数的实现标记为内联,这是完全正常。任何数量的文件都可以 #include 这个文件,而不会中断ODR。有趣的是,在类的主体中声明和定义的任何成员函数都是隐式内联的,所以如果你有一个这样的头:

The other major exception to the ODR involves inline functions. The spec specifically states that the ODR does not apply to inline functions, so if you have a header file with an implementation of a class member function that's marked inline, that's perfectly fine. Any number of files can #include this file without breaking the ODR. Interestingly, any member function that's declared and defined in the body of a class is implicitly inline, so if you have a header like this:

#ifndef Include_Guard
#define Include_Guard

class MyClass {
public:
    void DoSomething() {
        /* ... code goes here ... */
    }
};

#endif

然后你不会有破坏ODR的风险。如果你重写为

Then you're not risking breaking the ODR. If you rewrite this as

#ifndef Include_Guard
#define Include_Guard

class MyClass {
public:
    void DoSomething();
};

void MyClass::DoSomething()  {
    /* ... code goes here ... */
}

#endif

那么你打破ODR,因为成员函数没有被标记如果多个客户端 #include 此文件将有多个定义 MyClass :: DoSomething

then you would be breaking the ODR, since the member function isn't marked inline and if multiple clients #include this file there will be multiple definitions of MyClass::DoSomething.

所以总结一下 - 你应该把你的类分成.h / .cpp对,以避免破坏ODR。然而,如果你正在写一个类模板,你不需要.cpp文件(并且可能不应该有一个),如果你可以标记每一个成员函数的类inline你也可以避免使用.cpp文件。

So to summarize - you should probably split up your classes into a .h/.cpp pair to avoid breaking the ODR. However, if you're writing a class template, you don't need the .cpp file (and probably shouldn't have one at all), and if you're okay marking every single member function of your class inline you can also avoid the .cpp file.

这篇关于是一个好的做法是把C ++类的定义放到头文件中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆