在头文件VS实现(.cpp)文件中定义构造函数

时间:2022-09-08 19:32:24

Hi there and thanks for reading. This might be just falling under the 'personal preferences' category but anyway here we go...

嗨,谢谢你的阅读。这可能只属于“个人喜好”类别,但无论如何我们去...

I can define the body of a class constructor in the class .h file or in the implementation file .cpp. These two styles are probably identical as far as the compiler is concerned within a specific project (project for me means dll). Same applies to any member functions really: they can be defined in the header file or just declared there and then defined in the cpp file.

我可以在类.h文件或实现文件.cpp中定义类构造函数的主体。就特定项目中的编译器而言,这两种样式可能是相同的(项目对我来说意味着dll)。这同样适用于任何成员函数:它们可以在头文件中定义,也可以在那里声明,然后在cpp文件中定义。

HOWEVER...

然而...

I found that if I need to include such class header file(s) in different projects (meaning that ultimately the code that uses the header file ends up in a different dll) then having the actual implementation in the header file causes some headaches at compilation (not at linking... I don't even get to that point). Why? Well I won't go too much in detail but the compiler obviously tries to resolve all the functions which might be defined in other header files etc... forcing the poor developer to start pulling in various header files eetc...

我发现如果我需要在不同的项目中包含这样的类头文件(意味着最终使用头文件的代码最终在另一个dll中),那么在头文件中实际实现会导致编译时出现一些令人头疼的问题(不是在联系......我甚至没有达到这一点)。为什么?好吧,我不会详细说明,但编译器显然试图解决其他头文件中可能定义的所有函数等...迫使可怜的开发人员开始拉入各种头文件eetc ...

L O N G S T O R Y short:

长话短说:

Isn't it always best to keep header files free of any implementation and just use them for 'declarations'? That would make it easier to include them in more than one projects w/o having to carry around a lot of extra junk.

是不是总是最好保持头文件没有任何实现,只是将它们用于'声明'?这样可以更容易地将它们包含在多个项目中,而不必携带大量额外的垃圾。

What is your opinion about this? Thanks!

你对此有何看法?谢谢!

3 个解决方案

#1


21  

Keep your headers free of implementations, unless you want the implementations to be inlined (e.g. trivial getters/setters). And unless they're templates, of course.

保持标头不受实现影响,除非您希望实现内联(例如,琐碎的getter / setter)。当然,除非它们是模板。

I see no reason to make an exception for constructors. Put them in the .cpp file.

我认为没有理由为构造函数做一个例外。将它们放在.cpp文件中。

#2


18  

One important point to note is that if a member function is defined inside a header file, it must be either inside the class body or it must be marked explicitly as inline. In other words, it is plain wrong to do this in a header file:

需要注意的一点是,如果在头文件中定义了成员函数,它必须位于类体内,或者必须明确标记为内联。换句话说,在头文件中执行此操作是完全错误的:

class A {
  public:
    A();
};

A::A() {
  // constructor body
}

The reason it's wrong is because it will make the compiler include the definition in each compilation unit, while it's obvious that any function must be defined only once. Here are correct ways to do the same thing:

它错误的原因是因为它会使编译器在每个编译单元中包含定义,而很明显任何函数只能定义一次。以下是执行相同操作的正确方法:

class A {
  public:
    inline A();
};

inline A::A() {
  // constructor body
}

Or:

要么:

class A {
  public:
    inline A() { // inline isn't required here, but it's a good style
     // constructor body
    }
};

In both cases the constructor is inline. The only correct way to make it a regular out-of-line function would be to define it in the implementation file, not in the header. This is the most important difference between these two approaches.

在这两种情况下,构造函数都是内联的。使其成为常规外联函数的唯一正确方法是在实现文件中定义它,而不是在头文件中定义它。这是这两种方法之间最重要的区别。

Now, it is worth noting that inlining is an optimization. And as always with optimizations, they are best avoided until proved necessary. Among other problems inlining can lead to, there is the compatibility problem: if you change the body of a function that is not inlined, you only need to recompile the unit where it is defined, and everyone starts using the new implementation right away. With inlined functions, you need to recompile every unit that includes the relevant header, which can be a pain, especially if the header is used across different projects by different people.

现在,值得注意的是内联是一种优化。并且一如既往地进行优化,最好避免它们,直到证明有必要。在内联可能导致的其他问题中,存在兼容性问题:如果更改未内联的函数体,则只需重新编译定义它的单元,并且每个人都立即开始使用新实现。使用内联函数,您需要重新编译包含相关标题的每个单元,这可能很痛苦,尤其是如果标题由不同的人在不同的项目中使用。

In other words, use regular out-of-line definitions wherever possible until it is proved by profiling that a particular function call is a performance bottleneck. The only reasonable exception to this rule are trivial setters and getters, and even with them it is better to be careful - one day they may become non-trivial and it will mean a lot of recompilation and compatibility breaking.

换句话说,尽可能使用常规的外部定义,直到通过分析特定函数调用是性能瓶颈来证明它为止。这个规则唯一合理的例外是琐碎的setter和getter,即使使用它们也最好小心 - 有一天它们可能变得非常重要,这意味着很多重新编译和兼容性破坏。

#3


1  

Another note to consider: any changes to a header file require rebuilding all files that include that header file. Most build systems will rebuild source (*.cpp/.cc) files that depend on the modified header file.

需要考虑的另一个注意事项:对头文件的任何更改都需要重建包含该头文件的所有文件。大多数构建系统将重建依赖于修改的头文件的源(* .cpp / .cc)文件。

If you change a method of a class defined in a header file, all sources files including the header file will be rebuilt. If you change a method in a source file, only the source file is rebuilt. This could be an issue for medium to larger projects.

如果更改头文件中定义的类的方法,则将重建包括头文件的所有源文件。如果更改源文件中的方法,则仅重建源文件。这可能是中型到大型项目的问题。

To simplify the build process, most methods of a class should be defined in a source file. Small methods and other candidates for inlining should be defined in the header file.

为简化构建过程,应在源文件中定义类的大多数方法。应在头文件中定义用于内联的小方法和其他候选项。

#1


21  

Keep your headers free of implementations, unless you want the implementations to be inlined (e.g. trivial getters/setters). And unless they're templates, of course.

保持标头不受实现影响,除非您希望实现内联(例如,琐碎的getter / setter)。当然,除非它们是模板。

I see no reason to make an exception for constructors. Put them in the .cpp file.

我认为没有理由为构造函数做一个例外。将它们放在.cpp文件中。

#2


18  

One important point to note is that if a member function is defined inside a header file, it must be either inside the class body or it must be marked explicitly as inline. In other words, it is plain wrong to do this in a header file:

需要注意的一点是,如果在头文件中定义了成员函数,它必须位于类体内,或者必须明确标记为内联。换句话说,在头文件中执行此操作是完全错误的:

class A {
  public:
    A();
};

A::A() {
  // constructor body
}

The reason it's wrong is because it will make the compiler include the definition in each compilation unit, while it's obvious that any function must be defined only once. Here are correct ways to do the same thing:

它错误的原因是因为它会使编译器在每个编译单元中包含定义,而很明显任何函数只能定义一次。以下是执行相同操作的正确方法:

class A {
  public:
    inline A();
};

inline A::A() {
  // constructor body
}

Or:

要么:

class A {
  public:
    inline A() { // inline isn't required here, but it's a good style
     // constructor body
    }
};

In both cases the constructor is inline. The only correct way to make it a regular out-of-line function would be to define it in the implementation file, not in the header. This is the most important difference between these two approaches.

在这两种情况下,构造函数都是内联的。使其成为常规外联函数的唯一正确方法是在实现文件中定义它,而不是在头文件中定义它。这是这两种方法之间最重要的区别。

Now, it is worth noting that inlining is an optimization. And as always with optimizations, they are best avoided until proved necessary. Among other problems inlining can lead to, there is the compatibility problem: if you change the body of a function that is not inlined, you only need to recompile the unit where it is defined, and everyone starts using the new implementation right away. With inlined functions, you need to recompile every unit that includes the relevant header, which can be a pain, especially if the header is used across different projects by different people.

现在,值得注意的是内联是一种优化。并且一如既往地进行优化,最好避免它们,直到证明有必要。在内联可能导致的其他问题中,存在兼容性问题:如果更改未内联的函数体,则只需重新编译定义它的单元,并且每个人都立即开始使用新实现。使用内联函数,您需要重新编译包含相关标题的每个单元,这可能很痛苦,尤其是如果标题由不同的人在不同的项目中使用。

In other words, use regular out-of-line definitions wherever possible until it is proved by profiling that a particular function call is a performance bottleneck. The only reasonable exception to this rule are trivial setters and getters, and even with them it is better to be careful - one day they may become non-trivial and it will mean a lot of recompilation and compatibility breaking.

换句话说,尽可能使用常规的外部定义,直到通过分析特定函数调用是性能瓶颈来证明它为止。这个规则唯一合理的例外是琐碎的setter和getter,即使使用它们也最好小心 - 有一天它们可能变得非常重要,这意味着很多重新编译和兼容性破坏。

#3


1  

Another note to consider: any changes to a header file require rebuilding all files that include that header file. Most build systems will rebuild source (*.cpp/.cc) files that depend on the modified header file.

需要考虑的另一个注意事项:对头文件的任何更改都需要重建包含该头文件的所有文件。大多数构建系统将重建依赖于修改的头文件的源(* .cpp / .cc)文件。

If you change a method of a class defined in a header file, all sources files including the header file will be rebuilt. If you change a method in a source file, only the source file is rebuilt. This could be an issue for medium to larger projects.

如果更改头文件中定义的类的方法,则将重建包括头文件的所有源文件。如果更改源文件中的方法,则仅重建源文件。这可能是中型到大型项目的问题。

To simplify the build process, most methods of a class should be defined in a source file. Small methods and other candidates for inlining should be defined in the header file.

为简化构建过程,应在源文件中定义类的大多数方法。应在头文件中定义用于内联的小方法和其他候选项。