我可以在类 .h 文件或实现文件 .cpp 中定义类构造函数的主体。对于特定项目中的编译器而言,这两种样式可能是相同的(项目对我来说意味着 DLL )。 同样适用于任何成员函数:它们可以在头文件中定义,也可以在那里声明,然后在cpp文件中定义。
然而,我发现如果我需要在不同的项目中包含这样的类头文件(意味着最终使用头文件的代码最终在不同的 DLL 中),那么头文件中的实际实现会导致编译时出现一些令人头疼的问题(而不是链接...我甚至没有达到这一点)。 为什么?好吧,我不会详细说明,但编译器显然会尝试解决其他头文件等中可能定义的所有函数,迫使可怜的开发人员开始引入各种头文件等。
保持头文件没有任何实现并且只是将它们用于'声明'并不总是最好吗?这样可以更容易地将它们包含在多个项目中,而不必携带大量额外的垃圾。
您对此有何看法?
答案 0 :(得分:26)
保持标头免于实现,除非您希望实现内联(例如,琐碎的getter / setter)。当然,除非它们是模板。
我认为没有理由为构造函数设置异常。将它们放在.cpp文件中。
答案 1 :(得分:24)
需要注意的一点是,如果在头文件中定义了成员函数,它必须位于类体内,或者必须明确标记为inline
。换句话说,在头文件中执行此操作是完全错误的:
class A {
public:
A();
};
A::A() {
// constructor body
}
它错误的原因是因为它会使编译器在每个编译单元中包含定义,而很明显任何函数只能定义一次。以下是做同样事情的正确方法:
class A {
public:
inline A();
};
inline A::A() {
// constructor body
}
或者:
class A {
public:
inline A() { // inline isn't required here, but it's a good style
// constructor body
}
};
在这两种情况下,构造函数都是内联的。使其成为常规外联函数的唯一正确方法是在实现文件中定义它,而不是在头文件中定义它。这是这两种方法之间最重要的区别。
现在,值得注意的是内联是一种优化。并且一如既往地进行优化,最好避免它们,直到证明有必要。在内联可能导致的其他问题中,存在兼容性问题:如果更改未内联的函数的主体,则只需重新编译定义它的单元,并且每个人都立即开始使用新实现。使用内联函数,您需要重新编译包含相关标题的每个单元,这可能很麻烦,尤其是如果标题由不同的人在不同项目中使用时。
换句话说,尽可能使用常规的外联定义,直到通过分析特定函数调用是性能瓶颈来证明它为止。这个规则唯一合理的例外是微不足道的setter和getter,即使使用它们也最好小心 - 有一天它们可能变得非常重要,这将意味着大量的重新编译和兼容性破坏。
答案 2 :(得分:1)
需要考虑的另一个注意事项:对头文件的任何更改都需要重建包含该头文件的所有文件。大多数构建系统将重建依赖于修改的头文件的源(* .cpp / .cc)文件。
如果更改头文件中定义的类的方法,则将重建包括头文件在内的所有源文件。如果更改源文件中的方法,则仅重建源文件。这可能是中型到大型项目的问题。
为了简化构建过程,应该在源文件中定义类的大多数方法。应在头文件中定义小方法和其他内联候选者。