Reputation: 16605
I have used include guards many times before, but never really understood how or why they work.
Why doesn't the following work?
#ifndef CAMERA_CLASS_HPP
#define CAMERA_CLASS_HPP
class camera_class
{
....
};
camera_class glcam = camera_class();
#endif // CAMERA_CLASS_HPP
The error is this: (You can probably guess what it's going to be from the title of this question!)
-------------- Build: Debug in System ---------------
Linking console executable: bin/Debug/System
/usr/bin/ld: error: obj/Debug/main.o: multiple definition of 'glcam'
/usr/bin/ld: obj/Debug/camera_class.o: previous definition here
/usr/bin/ld: error: obj/Debug/main.glfunc.o: multiple definition of 'glcam'
/usr/bin/ld: obj/Debug/camera_class.o: previous definition here
collect2: ld returned 1 exit status
Process terminated with status 1 (0 minutes, 0 seconds)
0 errors, 0 warnings
Also, could someone please explain to me why a header guard works?
Upvotes: 3
Views: 1537
Reputation: 19032
It looks like you want to create a single glcam
object that can be used in multiple places. I would do that by exposing a free function to return a static
instance. This is similar to using extern
, but I find it to be a little more explicit in its intent.
#ifndef CAMERA_CLASS_HPP
#define CAMERA_CLASS_HPP
class camera_class
{
....
};
camera_class& get_camera();
#endif // CAMERA_CLASS_HPP
// in the CPP
camera_class& get_camera()
{
static camera_class the_camera;
return the_camera;
}
This gives you the ability to use a single camera_class
instance without relying on extern
, but at the same time doesn't force you to use it as a singleton, as other areas of the code are free to create their own private instances as well.
This could be implemented as it is (a free-function) or as a static
member function of the camera_class
. I chose the former, based on some excellent advice from Scott Meyers:
If you're writing a function that can be implemented as either a member or as a non-friend non-member, you should prefer to implement it as a non-member function.
Source: http://www.drdobbs.com/cpp/how-non-member-functions-improve-encapsu/184401197
Upvotes: 3
Reputation: 206518
Root Cause:
The header guard prevents inclusion of the same header multiple times in the same translation unit but not across different translation units. When you include the same header file in multiple translation units then,
A copy of glcam
is indeed being created in every translation unit where you include the header.
C++ standard mandates that each symbol can be defined only once(One Definition Rule) and hence the linker issues you the error.
Solution:
Do not create glcam
in the header file. Instead it should be created in such a way that it gets defined only once. The correct way to do this is by using the keyword extern
.
Upvotes: 4
Reputation: 21900
Since you are including this file from several files, you are breaking the One Definition Rule:
In the entire program, an object or non-inline function cannot have more than one definition
You should put the glcam
definition in a source file, rather than in a header file, or instead declare it as extern
, and provide a definition in some source file.
Upvotes: 2
Reputation: 121971
The header guard will prevent multiple inclusions in a single translation unit. The header can (and is) being included in multiple translation units:
// a.cpp:
#include "camera.hpp"
// b.cpp
#include "camera.hpp"
This will produce a a.obj
and a b.obj
, each containing a definition for glcam
. When linked together to produce the final binary you get the multiple definition error.
You need to declare glcam
in the header and define it exactly once in a .cpp
file:
// camera.hpp
...
extern camera_class glcam;
// camera.cpp
#include "camera.hpp"
camera_class glcam;
Upvotes: 8
Reputation: 308138
It's working just fine, you're only getting one definition in each of your source files.
The problem is that you have multiple source files and the linker is finding multiple definitions.
In the header file you should put:
extern camera_class glcam;
And then in one and only one source file put what you used to have in the header:
camera_class glcam = camera_class();
At this point you'll need to be aware of initialization order problems. Don't try to use glcam
from any static objects.
Upvotes: 0
Reputation: 65599
The include guard prevents multiple instances of the text in your header from appearing in one compilation unit (ie a single .cpp you're building which gets built into a .o)
It doesn't prevent multiple instances of that text from appearing in multiple compilation units.
So at link time, each compilation unit that includes this header has a
camera_class glcam = camera_class();
As a symbol. C++ can't decide when referring to "glcam" which single global definition you mean. The one from main.o or the one from camera_class.o?
Upvotes: 1