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

查看:192
本文介绍了将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 要节省空间:

Reason 1 To save space:

你有一个文件包含所有类成员的类的声明。您在此文件(或#pragma一次)上放置#include保护,以确保没有冲突发生,如果#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 arrise 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 链接器错误:

也许这是你从声明中分离实现的主要原因。在上面的示例中,您可以将方法体移动到类外部。这将使它看起来更清洁和结构。但是,根据此问题, a>,上述示例具有隐式 inline 说明符。将类中的实现从类中移动到类外部,如下面的示例,将导致链接器错误,因此您可以内联一切,或将函数定义移动到.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文件)。目前不允许(我假设)标准或当前编译器。与上述 原因2 的第一个示例不同,我们允许将实现放在头文件中。根据此问题,原因是模板类方法也暗示了 inline 说明符。那是对的吗?

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 底部的 .hpp c>文件, #include a .tpp 文件,包含任何 inline 方法。创建 .tpp 文件,并确保所有方法都指定为 inline

  • 3:所有其他成员(非内联函数,朋友函数和静态数据)应在 .cpp 文件中定义, code> #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),虽然我个人只是把inline函数在头文件的底部而不是一个单独的文件。

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::...

这种方法的原因是减少标头依赖,这会减慢大型项目的编译时间。如果你不知道,你可以转发声明一个类用作指针或引用。

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天全站免登陆