C++ class redefinition error - Help me understand headers and linking
Asked Answered
P

3

9

I started writing a simple interpreter in C++ with a class structure that I will describe below, but I quit and rewrote the thing in Java because headers were giving me a hard time. Here's the basic structure that is apparently not allowed in C++:

main.cpp contains the main function and includes a header for a class we can call printer.h (whose single void method is implemented in printer.cpp). Now imagine two other classes which are identical. Both want to call Printer::write_something();, so I included printer.h in each. So here's my first question: Why can I #include <iostream> a million times, even one after the other, but I can only include my header once? (Well, I think I could probably do the same thing with mine, as long as it's in the same file. But I may be wrong.) I understand the difference between a declaration and an implementation/definition, but that code gives me a class redefinition error. I don't see why. And here's the thing that blows my mind (and probably shows you why I don't understand any of this): I can't just include printer.h at the top of main.cpp and use the class from my other two classes. I know I can include printer.h in one of the two classes (headers) with no trouble, but I don't see why this is any different than just including it before I include the class in main.cpp (as doing so gives me a class not found error).

When I got fed up, I thought about moving to C since the OOP I was using was quite forced anyway, but I would run into the same problem unless I wrote everything in one file. It's frustrating to know C++ but be unable to use it correctly because of compilation issues.

I would really appreciate it if you could clear this up for me. Thanks!

Panpsychist answered 23/1, 2013 at 17:1 Comment(3)
Are you familiar with include/header guards (see en.wikipedia.org/wiki/Include_guard). The iostream library should have them and your printer.h header most likely does not. Header guards attempt to ensure that only one header definition is ever used.Hanseatic
I tried this initially but it didn't work. Maybe 'cause I only used them in one of the two headers.Panpsychist
Also, as a note after re-reading your question. This is not unique to C++, you would run into the same problem in C if printer.h declared a function and printer.c defined its implementation (you would still need header guards). For a similar question in C see: #5430519Hanseatic
C
11

Why can I #include a million times, even one after the other, but I can only include my header once?

It is probably because your header doesn't have an include guard.

// printer.h file
#ifndef PRINTER_H_
#define PRINTER_H_

 // printer.h code goes here

#endif

Note that it is best practice to chose longer names for the include guard defines, to minimise the chance that two different headers might have the same one.

Cristoforo answered 23/1, 2013 at 17:4 Comment(3)
Do I still have to include my normal header? I used this as a solution when I first had this problem but it didn't work, probably because I thought the naming was automatically connected to the actual header. (I thought PRINTER_H was the same as printer.h)Panpsychist
@AVR That should be your header. In other words, modify all your headers such that they all have include guards, with unique names of course.Cristoforo
Aha, should have payed attention to your // comment. And one more question: does this tell the compiler/linker to not redefine the class? I guess my problem was that the compiler didn't know better than to link the header to the implementation again.Panpsychist
L
5

Most header files should be wrapped in an include guard:

#ifndef MY_UNIQUE_INCLUDE_NAME_H
#define MY_UNIQUE_INCLUDE_NAME_H

// All content here.

#endif

This way, the compiler will only see the header's contents once per translation unit.

Lapel answered 23/1, 2013 at 17:4 Comment(2)
How does the include guard naming convention work? Do I include my normal header inside ifndef?Panpsychist
@AVR You would include the typical header definitions inside the ifndef. As for the naming convention question, see: https://mcmap.net/q/122048/-what-is-the-recommended-naming-convention-for-include-guardsHanseatic
S
0

C/C++ compilation is divided in compilation/translation units to generate object files. (.o, .obj)

see here the definition of translation unit

An #include directive in a C/C++ file results in the direct equivalent of a simple recursive copy-paste in the same file. You can try it as an experiment.

So if the same translation unit includes the same header twice the compiler sees that some entities are being defined multiple times, as it would happen if you write them in the same file. The error output would be exactly the same.

There is no built-in protection in the language that prevents you to do multiple inclusions, so you have to resort to write the include guard or specific #pragma boilerplate for each C/C++ header.

Stutz answered 23/1, 2013 at 19:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.