views:

320

answers:

5

Does it matter if the constructor/destructor implementation is provided in the header file or the source file? For example, which way is preferred and why?

Way 1:

class Singleton
{
  public:
    ~Singleton() { }

  private:
    Singleton() { }
};

Way 2:

class Singleton
{
  public:
    ~Singleton();

  private:
    Singleton();
};

In the source .cc file:

Singleton::Singleton()
{
}

Singleton::~Singleton()
{
}

Initially, I have the implementation in a source file, but I was asked to remove it. Does anyone know why?

+2  A: 

It does not matter, but usually it's better (in my humblest of opinions) to define them in the .cpp file, in order to hide the implementation from users of the class.

James McNellis
+1  A: 

If a function is defined in the header file (that is the function's body is in the header file), then the compiler can choose to inline the function as an optimization. If the function is defined in the source file, then the compiler can't inline it. Other than that, there really isn't any difference.

However, there are folks who would argue that you should put function definitions in the source file as much as possible to make it so that people who use the header file won't see the function definition.

Generally speaking, if you have short function which you think stands a good chance of being inlined, then it's a good idea to put it in the header file, so that the compiler can make that optimization. Otherwise, it's best to put it in the source file where it doesn't clutter things. And ideally, the header file shows the API, not the implementation, of the class anyway.

As for a singleton's constructor and destructor, they'll only be called once in the entire program, so you don't gain anything by inlining them, so you might as well stick them in the source file if you're doing much of anything in them. But if they're empty, why waste the space in the source file? Just put them in the header file.

Jonathan M Davis
If you add the function to the header without putting it in the class and without using the inline keyword, you'll get undefined symbols.
John Gordon
A compiler can (theoretically) inline methods defined in an implementation file using global program optimization.
D.Shawley
@John Quite true. @D.Shawley I'm unaware of any compilers actually doing that. That goes against how linking normally works, since you couldn't make such an optimization until linking occurs, and by that point, all the compilation has been done, so it would be too late to inline anything.
Jonathan M Davis
@Jonathan: Visual C++ can (and does in many cases) perform such inlining with link-time code generation enabled.
James McNellis
@JamesMcNellis: I wasn't sure if MSVC++ did this with link-time code generation or not. I have worked on a number of embedded compilers that enable link editing at the function level so that you did not have to be so careful about compilation units. Thanks for the info.
D.Shawley
Well, that's good to know, but it's certainly not something that you can generally count on. And once you add in dynamic libraries and the like, that naturally falls apart. So, it's a useful optimization if it's done, but I wouldn't count on it. Still, it's good to know that it's possible.
Jonathan M Davis
A: 

One difference is that adding the implementation directly in the class causes the function to be inlined.

John Gordon
No it does not. C++ does not provide a method to force inlining. An implementation is free to inline even if the method is defined in an implementation file... granted I don't know of one that will even with global optimizations.
D.Shawley
Correction _may cause the function to be inlined._ Which may or may not be desirable, especially for constructors and destructors.
Stephen
ya but that will just have a limited use..since the constructor will only be called in `getInstance` function where it will be inlined
Yogesh Arora
A: 

It matters in a few subtle ways. functions defined in the header file (your "way 1") may (In my experience, usually are) be declared inline (though, just as with inline, if the function is large it might not actually be inline by the compiler). This can cause problems, especially when dealing with abstract classes.

The reason for this is that the vtable will be included in the compilation unit (.o file) where the first non-inline function is found. If all of the functions are inlined, the v-table will not be found and your code will fail to link. (There are ways around this, but thats for another topic.)

So, use way 2 for more organized code, especially for classes with lots of member functions and/or long member functions, and in cases where you have inheritance and virtual functions floating around. Use way 1 for very short classes (where the entire header file will be less than around 100-200 lines).

SoapBox
And when I say "usually" I mean GCC seems to do it to me "all the time" (though there may be circumstances I'm not aware of when it doesn't, I don't disassemble everything I compile, and I haven't read the GCC source.)
SoapBox
A: 

The choice is largely stylistic as the function inlining may or may not come into play. Yes, everything in the header file may get inlined, but chances are, anything you know about function inlining is wrong. I know that everything I know about inilning is wrong as you just can't pick what the compiler will do. So I wouldn't worry about this aspect in this case.

There is, however another subtlety that we may explore here. Say the constructor in the Singleton class is public:

class Singleton
{
public:
    Singleton(void);
    virtual ~Singleton(void);
};
//cpp file
Singleton::Singleton()
{
}


Singleton::~Singleton()
{
}

If you put the above code into a static library what you have is essentially a class that cannot not be instantiated outside the library. It won't be the compiler that will prevent you from doing so but the linker. This is the error you will get:

1>testrun.obj : error LNK2001: unresolved external symbol "public: virtual __thiscall Singleton::~Singleton(void)" (??1Singleton@@UAE@XZ)
1>testrun.obj : error LNK2001: unresolved external symbol "public: __thiscall Singleton::Singleton(void)" (??0Singleton@@QAE@XZ)
1>C:\temp\sotest\Debug\testrun.exe : fatal error LNK1120: 2 unresolved externals

Igor Zevaka