Sign in to follow this  

Confused as to why this wont compile

This topic is 2052 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi Guys,

I am scratching my head at the moment with this one.

I have a class which I have stripped right back to bare bones trying to work out why it wont compile in my app.

[CODE]
#pragma once
class Asset
{
public:
Asset()
{
++(this->nID); // Asset ID (auto-increments)
}
~Asset()
{
}
private:
static int nID;
};
int Asset::nID=0;
[/CODE]

When I compile in my main application I get the following error;

[b]"private: static int Asset::nID" ([email="?nID@Asset@@0HA"]?nID@Asset@@0HA[/email]) already defined in framework.obj [/b]

I have taken the above and put it into a brand new C++ project and it compiles fine.

I cant understand why it wont compile in the 'primary' application I am working with.

Even if I change the nID variable to something else, then the new variable name is 'already defined'. So, it isn't a variable redefinition problem.

Any ideas on this would by hugely appreciated.

Share this post


Link to post
Share on other sites
Posted · Hidden
Hidden
Hi Guys,

I am scratching my head at the moment with this one.

I have a class which I have stripped right back to bare bones trying to work out why it wont compile in my app.

[CODE]
#pragma once
class Asset
{
public:
Asset()
{
++(this->nID); // Asset ID (auto-increments)
}
~Asset()
{
}
private:
static int nID;
};
int Asset::nID=0;
[/CODE]

When I compile in my main application I get the following error;

[b]"private: static int Asset::nID" ([email="?nID@Asset@@0HA"]?nID@Asset@@0HA[/email]) already defined in framework.obj [/b]

I have taken the above and put it into a brand new C++ project and it compiles fine.

I cant understand why it wont compile in the 'primary' application I am working with.

Even if I change the nID variable to something else, then the new variable name is 'already defined'. So, it isn't a variable redefinition problem.

Any ideas on this would by hugely appreciated.

Share this post


Link to post
Don't know if that's the whole header file you posted here but, in case it is, it's best practice to wrap the code in a header with "#ifndef" directives.

example: name_of_file.h
[CODE]
#ifndef NAME_OF_FILE_HEADER
#define NAME_OF_FILE_HEADER
//Put your code here...
#endif
[/CODE]

If you don't do this you may have conflict issues when compiling.

Good luck! Edited by TMarques

Share this post


Link to post
Share on other sites
[code]Source code (a.cpp, x.h) --(preprocessor)--> compilation_unit ---(compiler)--> a.o/.obj
Source code (b.cpp, x.h) --(preprocessor)--> compilation_unit ---(compiler)--> b.o/.obj --(linker)--> executable
Source code (c.cpp, x.h) --(preprocessor)--> compilation_unit ---(compiler)--> c.o/.obj[/code]

cpp file #includes header (.h) files. Compiler then converts stuff in those into code and symbols. nAssets is a symbol. At that point it's just a reference to something unknown, compiler doesn't care about it beyond a name.

Since each cpp is processed independently, we end up with 3 object files, each containing their own copy of nAssets. When linker puts all 3 together, it has 3 copies of nAssets.

By putting nAssets into .cpp file, such as (a.cpp), it will only generate one symbol, avoiding the confusion.

Header guard (pragma once) only prevents inclusion into same compilation unit. If x.h is included multiple times when preprocessing a.cpp, it will only appear the first time. But it will be included fully again when preprocessing b.cpp and c.cpp.

[quote]int Asset::nID=0;[/quote]This line actually says: "reserve 4 bytes inside executable at precisely this location. When someone needs nAssets symbol, give them this pointer".

Unlike most other languages, it's more than just semantic annotation, it's fairly important choice on where to put it.

[quote]it's best practice to wrap the code in a header with "#ifndef" directives.[/quote]

It is, but pragma once is fairly well supported these days and does the same. I prefer header guards myself. Edited by Antheus

Share this post


Link to post
Share on other sites
Putting the variable into a CPP file did the job.

I still don't understand why a straight cut and paste of the same code into a new project worked ok, though. Must be one of those strange compiler things.

Thanks guys, I was starting to tear my hair out.

Share this post


Link to post
Share on other sites
[quote name='lonewolff' timestamp='1335925102' post='4936633']
I still don't understand why a straight cut and paste of the same code into a new project worked ok, though. Must be one of those strange compiler things.
[/quote]
Definition in header will work as long as the header is included in only one CPP.
That's to say, the definition is in only one translate unit.
If the header is included in more than one CPPs, it won't link due to duplicated definition. Edited by wqking

Share this post


Link to post
Share on other sites
[quote name='lonewolff' timestamp='1335925102' post='4936633']
I still don't understand why a straight cut and paste of the same code into a new project worked ok, though. Must be one of those strange compiler things.

[/quote]

Because your new project probably either a) only includes the header once or b) you copied the class into a cpp file.

Read Antheus's post again.

Share this post


Link to post
Share on other sites
Hi guys,

The file [i]was[/i] included only once (in both projects) and was header only (no cpp file). Isnt it the job of #pragma once to stop multiple inclusions anyway (using VC2008)?

Anyway, it is working great now. Thanks guys.

Share this post


Link to post
Share on other sites
[quote name='lonewolff' timestamp='1335926281' post='4936642']
The file [i]was[/i] included only once (in both projects) and was header only (no cpp file). Isnt it the job of #pragma once to stop multiple inclusions anyway (using VC2008)?
[/quote]
It's nothing about multiple including.
Header only? At least your header will be include to any cpp file eventually to use, right?
Each cpp fill be expanded as a translate unit (with all headers are expanded to it).
If the definition is appearing in only one translate unit, that's fine. But if it's appearing in more than one translate unit, link error.

See One Definition Rule here
http://en.wikipedia.org/wiki/One_Definition_Rule

Share this post


Link to post
Share on other sites
[quote name='lonewolff' timestamp='1335926281' post='4936642']Isnt it the job of #pragma once to stop multiple inclusions anyway (using VC2008)?[/quote]

I strongly suggest to read Antheus post, because I really wish more people would actually care about how compiling and linking works.

#include doesn't mean "use this as a source to look up stuff". It means "recursively copy/paste the whole frigging thing and everything it includes". If in this whole mess of headers including headers including headers the same header appears twice, then and ONLY then pragma once or inclusion guards come into play.

Header files on their own are never compiled, so you can't have a project that is "header only". Nothing will really happen when compiling this.

Every .cpp file is compiled completely in isolation. It doesn't matter what might be in other files. Some compilers might make special exceptions for inline and template code, but you might notice that those are exactly the two things that should be entirely defined in header files, if you expect them to work.

After every .cpp file in your project has been compiled to an .obj file, the linker will try to piece them together and make the final binary (.dll, .exe). In THIS stage, you got your error about multiple definitions, long after anybody cares about headers or inclusions guards. All that matters is having the same definition in two different .obj files.

Share this post


Link to post
Share on other sites
As an additional note, this is not necessary when accessing nID. nID is not a non-static data member, therefore this has no effect on it. The difference between the three assignment lines:

[code]struct Thing {
Thing() {
Thing::oooh = 11;
oooh = 12;
this->oooh = 13;
}

static int oooh;
};
[/code]

Is a matter of clarity. I would personally prefer the first syntax, as it explicitly calls it out as a static member. The second syntax would be acceptable as well. The latter syntax, while valid, is extremely unclear and can introduce confusion.

Share this post


Link to post
Share on other sites
making a global var in headers dont work, even if you put it in a namespace, it doesn't work. if you make it static, it'll compile but it's value is only accessible by the class that set it's value. You must set it in a cpp, I hate using globals but if i do use them i keep them in main.cpp for easy reference Edited by Muzzy A

Share this post


Link to post
Share on other sites

This topic is 2052 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this