3

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?

2
  • 1
    Your understanding of what include guards do is mistaken. They prevent a header file being included twice by the same source file. They do not, and cannot, prevent a header being included twice by different source files. Commented Sep 7, 2012 at 14:53
  • Thanks John, I didn't understand that fundamental difference! Commented Sep 7, 2012 at 15:04

6 Answers 6

8

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; 
Sign up to request clarification or add additional context in comments.

1 Comment

This is the solution I chose. I don't like having an object of the camera_class in a completely different file (main.opengl.cpp) but it will have to do!
4

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.

6 Comments

Where should I create glcam? Should I create it in the .cpp file?
@EdwardBird: Updated to answer your query.
extern works, but I always have found that to be a little confusing. I prefer to use the method provided in my answer, which achieves basically the same result, but I find to be clearer. Obviously though, this is only a matter of taste.
What does extern do? Is it just a bodge to get it working? - I read the article, but not sure I understand it.
Ah I see, so it's like a linker 'switch' I guess you could say? Which just makes it behave differently. I think I'm going to have a look on Wikipedia about how C++ compilers work and what times what things are done. This should help me understand it better. :)
|
3

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

2 Comments

... Although another question. Why is camera_class& get_camera(); not a method inside the class? Why is it outside of the class definition?
Great question, and worthy of an answer in a more visible place than in a comment. See the last paragraph of my answer. That link has some excellent information that is specifically on topic for your original question.
2

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.

Comments

1

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?

Comments

0

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.