0

I'm more into JAVA, but when HI-Perf is in the requirements list C/C++ must come on the table. And so it did. And, as expected, I'd stumbled upon something I cannot understand and cannot dig out in SO. So can anyone explain me why didn't guardian in a header below work?

// a0a.h

#ifndef _A0A
#define _A0A

class State {
public:
    static State* A;
}

State* State::A = new State(); /* this is going to be troublesome */

#endif

.

B.h // #include "a0a.h"
C.h // #include "a0a.h"

XXX.h // #include "B.h"
XXX.h // #include "C.h"

and compilation gives me a multiple definitions error on the line pointed out above.

I've made it work by moving that line away to a .cpp source file, but I still want to understand why didn't guardian protect me from shooting my own foot here?

I hope I do not have to tell that this is just a dummy code reflecting what I did -- not the actual code I was writing.

EDIT

Repeated Multiple Definition Errors from including same header in multiple cpps does not explain WHY. I mean I do not get the picture from any of those answers -- they just give suggestions (none of which I need).

Of course I can dive into books and probably learn how all of this makes compiler sneeze in my console, but asking is SO is much faster and will most likely help someone else to understand.

Community
  • 1
  • 1
netikras
  • 260
  • 2
  • 8
  • 2
    The static must be defined in an implementation file (*.cpp). – zdf Jan 24 '17 at 19:51
  • 2
    ***why didn't guardian protect me from multiple definitions?*** Because an include guard only prevents multiple inclusion in the same translation unit (cpp source file). – drescherjm Jan 24 '17 at 19:54
  • Are you sure you have only one single .cpp file where you include your includes? – marcinj Jan 24 '17 at 19:54
  • That's not what guardians are used for. – Kerrek SB Jan 24 '17 at 19:55
  • Remember that `#include` literally just replaces that line with the contents of a file (pretty much state-of-the art in the 1960's). Every time you include that header, it's exactly the same as if the code was written there.. – molbdnilo Jan 24 '17 at 20:00
  • Btw, in 2017 we tend to use `#pragma once` – SergeyA Jan 24 '17 at 20:04
  • Also related: http://stackoverflow.com/q/249701/103167 – Ben Voigt Jan 24 '17 at 20:05
  • 2
    Also note that `_A0A` is an illegal symbol for you to use, because such an identifier is reserved for the compiler. Make it `A0A_H` or something like that. And "C/C++" is not a language. You are obviously only interested in C++ and not in C. – Christian Hackl Jan 24 '17 at 20:09
  • @ChristianHackl in this particular case - yes, I'm leaning towards c++. Usually I prefer plain old C :) Also _A0A was just an example. – netikras Jan 24 '17 at 20:14
  • @molbdnilo yes, saw that somewhere. kind of *inline*, just at a file scope. However if #include inlines a file with all those #if[n]def - shouldn't other but the first inlines be ignores? I mean that _A0A thingie is already there after the first include. – netikras Jan 24 '17 at 20:19
  • @drescherjm I always imagined that compiler simply joins all #included files in a single huge header-like ..umm... "file" (let's call it that for sake of simplicity). And then uses that "file" when compiling sources. I must be wrong about that, right? – netikras Jan 24 '17 at 20:21
  • 2
    Each source file is compiled separately. With the headers that it included. – drescherjm Jan 24 '17 at 20:26
  • @netikras Each .cpp file that includes the header is compiled entirely separately – no information is saved from the compilation of one file to the compilation of another. It is exactly the same as if you had written `State* State::A = new State();` in every .cpp file instead of in a header. – molbdnilo Jan 24 '17 at 20:31
  • 3
    In short - header files are for declaration, not for definition. – Slava Jan 24 '17 at 20:32
  • @molbdnilo so the error happens when .cpp sources are brought together? we don't have ifndefs there any more... it starts to make sense now... ...header files have been already inlined in cpp and guardian is consumed at single-source level. So the error probably happens when concatenating sources (.cpp or .o?), as compiler comes across identical definition. but that happens in cpp (.o?), not .h . Damn, I must have figured it all wrong :) – netikras Jan 24 '17 at 20:41
  • @Slava is that always the case? Isn't it values in headers we are changing when recompiling Linux with custom settings? – netikras Jan 24 '17 at 20:42
  • 1
    @netikras no, that not always the case, this is just recommendation for beginners, you need to understand what you are doing when you break it. – Slava Jan 24 '17 at 20:45
  • @Slava: That's *too* short. Because that would mean that you couldn't define classes in header files, which would break C++ as we know it. – Christian Hackl Jan 24 '17 at 21:18
  • @ChristianHackl you do not define classes, you declare them. – Slava Jan 24 '17 at 21:19
  • In short, I'd recommend that you read up on how the different translation phases work in C++. Preprocessing => Compiling => Linking. There's a lot of material on that subject, and you probably can even start at Wikipedia. – Christian Hackl Jan 24 '17 at 21:19
  • @Slava: No. You declare the class member functions, but you define the class. I.e. `class X;` is a declaration, but `class X {};` is a definition. – Christian Hackl Jan 24 '17 at 21:20
  • @ChristianHackl `class X;` is **forward delcaration**. Open this documentation http://en.cppreference.com/w/cpp/language/class and look what subject tells you. – Slava Jan 24 '17 at 21:21
  • @Slava: "Forward declaration" is more or less an informal term. It's really called *declaration*. – Christian Hackl Jan 24 '17 at 21:23
  • @Slava: See the C++ standard starting at §9.1. *"A class definition introduces a new type."* One of the examples: `struct X { int a; };`. – Christian Hackl Jan 24 '17 at 21:24
  • @ChristianHackl "rule" I gave is informal as well. – Slava Jan 24 '17 at 21:31
  • @Slava: I know, but as I said, it's really a stretch. Because `class X { void f(); };` **is** by all means a definition, and not a declaration. `void f();` inside of it, however, is a declaration. And `void X::f() {}` is a definition again. The ODR specifically distinguishes between different kinds of definitions; see §3.2/6, *"There can be more than one definition of a class type (...)"*. – Christian Hackl Jan 24 '17 at 21:36
  • @ChristianHackl I know about poor design of C++ standard (and probably language), that you cannot explain simple things by simple words to a novice. Yes my "rule" is oversimplified, but gives general idea to the OP. – Slava Jan 24 '17 at 21:39
  • @Slava: Well, you *did* then say that "you do not define classes, you declare them", which just isn't correct. So there was at least the valid assumption that your guideline was intended quite literally, and not just as an oversimplification. I do believe that things should be explained as simple as possible, but not simpler than that! :) And C++ is extremely complex, so "as simple as possible" is often just not very simple, for better or worse. – Christian Hackl Jan 24 '17 at 21:52

1 Answers1

0
  1. The "guardian" avoids multiple inclusions in the same file.
  2. Each time you include the header, the definition of State::A is included. If you included multiple times, you will have multiple definitions for State::A, which is not legal: the linker will issue errors. To avoid this, define State::A in an implementation file (*.cpp).
zdf
  • 3,363
  • 2
  • 14
  • 25