I think that I misunderstood something about class definitions in c++...
Why it is legal to define class in multiple files, like this:

file Test.cpp:

#include <iostream>

using namespace std;

void testFunc();

class A
{
public:
	void printText();
} clA;

void A::printText()
{
	cout << "file: Test.cpp, class: A, method: void printText" << endl;
}

int main()
{
	clA.printText();
	testFunc();

	getchar();

	return 0;
}

file NewFile.cpp:

#include <iostream>

using namespace std;

void testFunc();

class A
{
public:
	int printText();
} clB;

int A::printText()
{
	cout << "file: NewFile.cpp, class: A, method: int printText" << endl;

	return 0;
}

void testFunc()
{
	clB.printText();
}

Do You see that there are definitions for class A, but the methods are little bit dirrefent: void printText and int printText. This project will compile just fine, but if we try to put these methods in one definition file, there will be an error:

class A
{
public:
	void printText();
	int printText();
}

So why we can overcome this and create class definition in more than one file?
I thought that maybe class A from Test.cpp and class A from NewFile.cpp are not the same classes, and then clA and clB are objects of different types, but if we check these objects via typeid, then we see that they are of the same type...
Also, the clA and clB objects are of the same type (if we check it via typeid).

Recommended Answers

All 14 Replies

Would you summarize your question, I have failed to understand it

Summarize: why it is legal to do so? Could it be useful is some situations? Why it compiles fine, when there are in the class two methods with different return types only?

>>So why we can overcome this and create class definition in more than one file?

Put the class declaration in a header file with *.h extension and include the header file in all *.cpp files that use it. The implementation code goes only in one *.cpp file.


>>Summarize: why it is legal to do so?
The compiler processes only one *.cpp file at a time. So if you add the class implementation code in multiple files the compiler won't know it until the linker attempts to put everything together. Then it will tell you that you have included the implementation code in multiple files.

>>The compiler processes only one *.cpp file at a time. So if you add the class implementation code in multiple files the compiler won't know it until the linker attempts to put everything together. Then it will tell you that you have included the implementation code in multiple files.

Well, I think that's not exactly truth... My code above is compiled and linked without any errors. There will be an error if there will be more than one definition in one compilation (translation) unit. Here is just one definition in one translation unit.

I need to add one thing:
I think, that in situation like above, the compiler should claim about multiple definition of A::printText(). That's what the gcc compiler does. Unfortunatelly VC++ doesn't behave like that. Now I wonder if these situations are described in C++ standard?

The first version works because they are not #included to each other. As a result, the class definitions are not visible outside their own files/modules. If you add #include "NewFile.cpp" to the top of your "test.cpp" file, you will get compile errors concerning class redefinition. This applies to variable names as well. Try to access clA.printText() in testFunc() and you will get an error "'clA': undeclared identifier" (or similar, depending on your compiler).

Also AD is correct. The compiler only processes one file at a time, and does not keep track of the contents of more than one file at a time, unless it hits a #include. It's the linker that takes the individually compiled files and puts them together into a working program. Since your class(es) are defined in source code files (*.cpp files) instead of #included from header files (*.h files), there is no way for either of them to know otherwise.

Ok, but shouldn't it results in compilation time error, just like it is in gcc? After all, after linking there are two methods with the same signature, but different return types...

If you compile Test.cpp and NewFile.cpp separately, then attempt to link then the linker should complain about multiple occurrences of A::printText() function. If it doesn't then your compiler may be just silently merging them.

Ok, but shouldn't it results in compilation time error,

No because as I said before the compiler only works on one *.c or *.cpp file at a time (unless one is included in another, which is a horrible thing to do).

After all, after linking there are two methods with the same signature, but different return types...

Return types don't differentiate methods or functions. Parameters are what makes then different.

Return types don't differentiate methods or functions. Parameters are what makes then different.

Correct, that's why I was so astonished that this works with VC++. But maybe this is how You wrote earlier:

If you compile Test.cpp and NewFile.cpp separately, then attempt to link then the linker should complain about multiple occurrences of A::printText() function. If it doesn't then your compiler may be just silently merging them.

I see two things that could be happening that makes it compile without error in VC++:

1. VC++ might actually mangle the names differently according to return type, the standard allows it even though it really serves no purpose because overloaded return type are not supported, so who knows, maybe for the VC++ linker these two functions are not ambiguous at link-time.

2. Most probably, the linker looks into one object file at a time to resolve its external symbols and once it finds a match it is satisfied and does not look any further, such as to see if there is a multiple occurrence of the symbol (which is a bit of a waste of time). This actually happened for me with GCC, there was an old version of an object file put in one lib and the newer one in another and I was getting a ton of seg-faults and stuff because the linker was linking the old version and was not complaining that there were all the same external symbols in another library (just because of the order they were send to the compiler). But still, in the same module, the multiple occurring symbols should be detecting during the construction of the symbol table, unless VC++ does not make a lib-wide symbol table when all the object files go into an executable.

Microsoft name mangling is a very complex topic. After a littled testing of the code in the original post to this thread I've come to the conclusion that Microsoft does include the return type as part of the mangled name. The compiler allows both versions of the function because names are mangled at compile time, not link time. Since the two classes are in two different *.cpp files the compiler has no way of knowing that the two functions differ only by return type. If you put both functions in the same *.cpp file the compiler generates the error message error C2556: 'void A::printText(void)' : overloaded function differs only by return type from 'int A::printText(void) .

To further complicate things, add a contructor to each version of the class and print out a different message in each constructor. Only one of the two contructors is called for both clA and clB objects.

Thanks, Ancient Dragon and mike_2000_17. That's make sense - I think that VC++ really includes return type to the mangled name, and the test with constructors completely ruined my mind. Maybe I will not dig to deep with this topic, or I will go nuts.
Thank!
PS.: Are there any good books about how C++ compilers works?

>>Are there any good books about how C++ compilers works?

I used to know a good site that really went through compiler design and related tutorials very nicely (once had a wild dream of writing a compiler myself), but I can't find it anymore. I think this Stanford CS course has a whole bunch of lecture notes that go through it well. There are also more advanced courses on compilers there too.

But in this case, the problem discussed was more in relation to the Linker (which works after the compiler). I don't know that there is much public literature on Linkers (like "Linkers for dummies", lol) because the basic idea of it just boils down to taking the list of symbols the compiler produced, look through the compiled code for undefined external references (functions that are not found in the compilation unit and thus, need to be linked to some other object file / library / DLL), and then doing symbol look-ups and associating the external references to the functions that were found in external libraries. Of course, there is much more to it, but that basic knowledge is enough for practical purposes when coding (you would really only need to know more if you plan to work later on designing compilers and linkers yourself or on some other high-end CS field, in which case you would have to learn much more than what books or textbooks could tell you about it).

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.