如果头文件或源文件中提供了构造函数/析构函数实现,这是否重要?例如,哪种方式更受欢迎?为什么?
方式1:
class Singleton
{
public:
~Singleton() { }
private:
Singleton() { }
};
方式2:
class Singleton
{
public:
~Singleton();
private:
Singleton();
};
在源.cc文件中:
Singleton::Singleton()
{
}
Singleton::~Singleton()
{
}
最初,我在源文件中有实现,但我被要求删除它。有谁知道为什么?
答案 0 :(得分:2)
没关系,但通常最好(在我最卑鄙的意见中)在.cpp文件中定义它们,以便隐藏类的用户的实现。
答案 1 :(得分:1)
如果在头文件中定义了一个函数(即函数的主体在头文件中),则编译器可以选择将inline函数作为优化。如果在源文件中定义了该函数,则编译器无法对其进行内联。除此之外,确实没有任何区别。
然而,有些人会争辩说你应该尽可能地将函数定义放在源文件中,以便使用头文件的人不会看到函数定义。
一般来说,如果你有一个很短的函数,你认为很有可能被内联,那么把它放在头文件中是个好主意,这样编译器就可以进行优化。否则,最好将它放在源文件中,它不会使事情变得混乱。理想情况下,头文件无论如何都会显示API,而不是实现。
对于单例的构造函数和析构函数,它们只会在整个程序中被调用一次,所以你没有通过内联它们获得任何东西,所以如果你正在做的话你也可以将它们粘在源文件中其中很多东西。但如果它们是空的,为什么要浪费源文件中的空间呢?只需将它们放在头文件中即可。
答案 2 :(得分:0)
以一些微妙的方式重要。头文件中定义的函数(你的“方式1”)可能(根据我的经验,通常是)内联声明(但是,与内联一样,如果函数很大,它可能实际上不是由编译器内联)。这可能会导致问题,尤其是在处理抽象类时。
原因是vtable将包含在编译单元(.o文件)中,其中找到第一个非内联函数。如果内联所有函数,则无法找到v表,并且您的代码将无法链接。 (有很多方法可以解决这个问题,但那是另一个话题。)
因此,对于更有组织的代码,使用方式2,特别是对于具有大量成员函数和/或长成员函数的类,以及在具有继承和虚函数的情况下。使用方法1用于非常短的类(整个头文件将小于约100-200行)。
答案 3 :(得分:0)
选择主要是风格,因为功能内联可能会或可能不会发挥作用。是的,头文件中的所有内容都可以内联,但很可能,您对函数内联的任何了解都是错误的。我知道我所知道的关于inilning的一切都是错误的,因为你无法选择编译器会做什么。所以在这种情况下我不会担心这个方面。
然而,我们可以在这里探讨另一个微妙之处。假设Singleton类中的构造函数是public:
class Singleton
{
public:
Singleton(void);
virtual ~Singleton(void);
};
//cpp file
Singleton::Singleton()
{
}
Singleton::~Singleton()
{
}
如果您将上述代码放入静态库中,那么您所拥有的本质上是一个无法在库外实例化的类。除了链接器之外,编译器不会阻止您这样做。这是您将得到的错误:
1> testrun.obj:错误LNK2001:未解析的外部符号“public:virtual __thiscall Singleton :: ~Singleton(void)”(?? 1Singleton @@ UAE @ XZ)
1> testrun.obj:错误LNK2001:未解析的外部符号“public:__thiscall Singleton :: Singleton(void)”(?? 0Singleton @@ QAE @ XZ)
1> C:\ temp \ sotest \ Debug \ testrun.exe:致命错误LNK1120:2个未解析的外部
答案 4 :(得分:-1)
一个区别是直接在类中添加实现会导致函数内联。