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

时间:2011-01-21 17:26:14

标签: c++ constructor header-files

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

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

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

您对此有何看法?

3 个答案:

答案 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)文件。

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

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