将C ++类代码分成多个文件,有哪些规则? [英] Separating C++ Class Code into Multiple Files, what are the rules?

查看:184
本文介绍了将C ++类代码分成多个文件,有哪些规则?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

正如标题所示,我遇到的最终问题是多个定义链接器错误。我已经解决了这个问题,但我没有以正确的方式解决问题。在开始之前,我想讨论将类文件拆分成多个文件的原因。我试图把所有可能的场景放在这里 - 如果我错过任何一个,请提醒我,我可以做出改变。希望以下内容是正确的:

As the title suggests, the end problem I have is multiple definition linker errors. I have actually fixed the problem, but I haven't fixed the problem in the correct way. Before starting I want to discuss the reasons for splitting a class file into multiple files. I have tried to put all the possible scenarios here - if I missed any, please remind me and I can make changes. Hopefully the following are correct:

原因1 节省空间:

您有一个包含所有类成员的类声明的文件。您可以在此文件周围放置#include防护(或#pragma一次),以确保在#include包含在源文件中的两个不同头文件中的文件时不会发生冲突。您可以使用此类中声明的任何方法的实现来编译单独的源文件,因为它会从源文件中加载许多行代码,这会清除一些内容并为您的程序引入一些顺序。

You have a file containing the declaration of a class with all class members. You place #include guards around this file (or #pragma once) to ensure no conflicts arise if you #include the file in two different header files which are then included in a source file. You compile a separate source file with the implementation of any methods declared in this class, as it off loads many lines of code from your source file, which cleans things up a bit and introduces some order to your program.

示例:如您所见,可以通过将类方法的实现拆分为不同的文件来改进以下示例。 (.cpp文件)

// my_class.hpp
#pragma once

class my_class
{
public:
    void my_function()
    {
        // LOTS OF CODE
        // CONFUSING TO DEBUG
        // LOTS OF CODE
        // DISORGANIZED AND DISTRACTING
        // LOTS OF CODE
        // LOOKS HORRIBLE
        // LOTS OF CODE
        // VERY MESSY
        // LOTS OF CODE
    }

    // MANY OTHER METHODS
    // MEANS VERY LARGE FILE WITH LOTS OF LINES OF CODE
}

原因2 防止多重定义链接器错误:

也许这就是为什么要从声明中拆分实现的主要原因。在上面的示例中,您可以将方法主体移动到类外部。这将使它看起来更清洁和结构化。但是,根据这个问题,上面的示例具有隐式内联说明符。将实现从类中移动到类外部(如下例所示)将导致链接器错误,因此您可以内联所有内容,或将函数定义移动到.cpp文件。

Perhaps this is the main reason why you would split implementation from declaration. In the above example, you could move the method body to outside the class. This would make it look much cleaner and structured. However, according to this question, the above example has implicit inline specifiers. Moving the implementation from within the class to outside the class, as in the example below, will cause you linker errors, and so you would either inline everything, or move the function definitions to a .cpp file.

示例:_如果不将函数定义移动到.cpp文件或将函数指定为内联,则下面的示例将导致多个定义链接器错误。

Example: _The example below will cause "multiple definition linker errors" if you do not move the function definition to a .cpp file or specify the function as inline.

// my_class.hpp
void my_class::my_function()
{
    // ERROR! MULTIPLE DEFINITION OF my_class::my_function
    // This error only occurs if you #include the file containing this code
    // in two or more separate source (compiled, .cpp) files.
}

解决问题:

//my_class.cpp
void my_class::my_function()
{
    // Now in a .cpp file, so no multiple definition error
}

或者:

// my_class.hpp
inline void my_class::my_function()
{
    // Specified function as inline, so okay - note: back in header file!
    // The very first example has an implicit `inline` specifier
}

原因3 您想再次节省空间,但这次您正在使用模板类:

如果我们正在处理模板类,那么我们就无法将实现移动到源文件(.cpp文件)。目前,(我假设)标准或当前编译器都不允许这样做。与上面 Reason 2 的第一个示例不同,我们允许将实现放在头文件中。根据这个问题,原因是模板类方法也隐含了内联说明符。那是对的吗? (这似乎有道理。)但似乎没有人知道我刚引用的问题!

If we are working with template classes, then we cannot move the implementation to a source file (.cpp file). That's not currently allowed by (I assume) either the standard or by current compilers. Unlike the first example of Reason 2, above, we are allowed to place the implementation in the header file. According to this question the reason is that template class methods also have implied inline specifiers. Is that correct? (It seems to make sense.) But nobody seemed to know on the question I have just referenced!

那么,以下两个例子是否相同?

So, are the two examples below identical?

// some_header_file.hpp
#pragma once

// template class declaration goes here
class some_class
{
    // Some code
};

// Example 1: NO INLINE SPECIFIER
template<typename T>
void some_class::class_method()
{
    // Some code
}

// Example 2: INLINE specifier used
template<typename T>
inline void some_class::class_method()
{
    // Some code
}

如果你有一个模板类头文件,由于你拥有的所有函数而变得庞大,那么我相信你可以将函数定义移动到另一个头文件(通常是.tpp)文件?)然后 #include file.tpp 在包含类声明的头文件的末尾。但是,您不得在其他任何地方包含此文件,因此 .tpp 而不是 .hpp

If you have a template class header file, which is becoming huge due to all the functions you have, then I believe you are allowed to move the function definitions to another header file (usually a .tpp file?) and then #include file.tpp at the end of your header file containing the class declaration. You must NOT include this file anywhere else, however, hence the .tpp rather than .hpp.

我假设您也可以使用常规类的内联方法执行此操作?这也允许吗?

I assume you could also do this with the inline methods of a regular class? Is that allowed also?

所以我上面做了一些陈述,其中大部分与源文件的结构。我认为我所说的一切都是正确的,因为我做了一些基础研究并发现了一些东西,但这是一个问题,所以我不确定。

So I have made some statements above, most of which relate to the structuring of source files. I think everything I said was correct, because I did some basic research and "found out some stuff", but this is a question and so I don't know for sure.

归结为,您将如何在文件中组织代码。我想我已经想出了一个永远有效的结构。

What this boils down to, is how you would organize code within files. I think I have figured out a structure which will always work.

这是我想出的。 (这是Ed Bird的班级代码文件组织/结构标准,如果你愿意的话。不知道它是否会非常有用,这就是要求。)

Here is what I have come up with. (This is "Ed Bird's class code file organization / structure standard", if you like. Don't know if it will be very useful yet, that's the point of asking.)


  • 1: .hpp 文件,包括所有方法,朋友功能和数据。

  • 2: .hpp file, #include 一个 .tpp 文件包含任何内联的实现方法。创建 .tpp 文件并确保所有方法都指定为内联

  • 3:所有其他成员(非内联函数,友元函数和静态数据)应在 .cpp 文件中定义,其中 #include 位于顶部的 .hpp 文件,以防止出现ABC类尚未声明等错误。由于此文件中的所有内容都有外部链接,因此程序将正确链接。

  • 1: Declare the class (template or otherwise) in a .hpp file, including all methods, friend functions and data.
  • 2: At the bottom of the .hpp file, #include a .tpp file containing the implementation of any inline methods. Create the .tpp file and ensure all methods are specified to be inline.
  • 3: All other members (non-inline functions, friend functions and static data) should be defined in a .cpp file, which #includes the .hpp file at the top to prevent errors like "class ABC has not been declared". Since everything in this file will have external linkage, the program will link correctly.

这样的标准是否存在于行业中?我会在所有情况下提出标准吗?

Do standards like this exist in industry? Will the standard I came up with work in all cases?

推荐答案

你的三点听起来是正确的。这是做标准的标准方法(尽管之前我没有看过.tpp扩展名,通常是.inl),虽然我个人只是将内联函数放在头文件的底部而不是单独的文件中。

Your three points sound about right. That's the standard way to do things (although I've not seen .tpp extension before, usually it's .inl), although personally I just put inline functions at the bottom of header files rather than in a separate file.

以下是我安排文件的方式。我省略了简单类的前向声明文件。

Here is how I arrange my files. I omit the forward declare file for simple classes.

myclass-fwd.h

myclass-fwd.h

#pragma once

namespace NS
{
class MyClass;
}

myclass.h

myclass.h

#pragma once
#include "headers-needed-by-header"
#include "myclass-fwd.h"

namespace NS
{
class MyClass
{
    ..
};
}

myclass.cpp

myclass.cpp

#include "headers-needed-by-source"
#include "myclass.h"

namespace
    {
    void LocalFunc();
}

NS::MyClass::...

根据首选项将标头保护替换为pragma ..

Replace pragma with header guards according to preference..

这种方法的原因是减少标头依赖性,这会减慢大型项目中的编译时间。如果您不知道,可以转发声明一个类以用作指针或引用。只有在构造,创建或使用类的成员时才需要完整声明。

The reason for this approach is to reduce header dependencies, which slow down compile times in large projects. If you didn't know, you can forward declare a class to use as a pointer or reference. The full declaration is only needed when you construct, create or use members of the class.

这意味着另一个使用该类的类(仅通过指针/引用获取参数)必须在自己的标题中包含fwd标头。然后,完整的标头包含在第二个类的源文件中。这大大减少了拉入一个大头时所需的垃圾量,这会引入另一个大头,这会引入另一个大头...

This means another class which uses the class (takes parameters by pointer/reference) only has to include the fwd header in its own header. The full header is then included in the second class's source file. This greatly reduces the amount of unneeded rubbish you get when pulling in a big header, which pulls in another big header, which pulls in another...

下一个提示是未命名的命名空间(有时称为匿名命名空间)。这只能出现在源文件中,它就像一个只对该文件可见的隐藏命名空间。您可以在此处放置仅由源文件使用的本地函数,类等。如果您在两个不同的文件中创建具有相同名称的内容,则可以防止名称冲突。 (例如,两个本地函数F可能会给链接器错误。)

The next tip is the unnamed namespace (sometimes called anonymous namespace). This can only appear in a source file and it is like a hidden namespace only visible to that file. You can place local functions, classes etc here which are only used by the the source file. This prevents name clashes if you create something with the same name in two different files. (Two local function F for example, may give linker errors).

这篇关于将C ++类代码分成多个文件,有哪些规则?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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