Translation unit and classes

Started by
9 comments, last by ryt 5 years, 7 months ago

As I'm reading C++ Programming Language and some other tutorials I came on Translation Unit part. I got a bit confused how they work with classes. What is rally not clear to me is inclusion of class headers in different other files that need them. Somewhere in these tutorials I red that each time when we #include a class header, that the current translation unit has its own class. From that I understood that even if we use include guards, that different files, that include same classes, have their own definitions of classes.

So for e.g. let's say that we have two classes A and B. Another two separate files Alpha and Beta, where they include both classes. A third file main.cpp that includes Alpha and Beta, but knows nothing of A and B.
From what I understood is that in main.cpp we will have two definitions of A and two of B, even if A and B use include guards.

Is this true or I confused something?

Advertisement

You are pretty close, actually! What makes this work is that the header just contains the declaration of the class, not it's actual implementation. All the compiler sees is the promise that there is a class named A with the following functions and it can include references to these functions. The actual implementation file, Alpha or Beta in your case, only contain references to the class and it's functions but not it's actual code. That would come from the .cpp file. It's up to the linker at the end to merge the compiled code for the class itself with the references from Alpha and Beta.

It's different if the code is directly included in the header file for the classes. If they are not templated and not inlined, the linker will actually complain about multiple definitions of the same function existing in both Alpha and Beta. That's because each translation unit actually did emit code for the class. When inlining, there is no actual function generated and instead the code of the function is directly emitted in the callsite, so no duplicate functions here. Lastly, templates: The compiler has to see the full definition when instantiating a template, so if Alpha and Beta both include class A and instantiate it as A<int>, they will both generate the same code for A<int>. In this case however, the linker will not complain about duplicate implementations of A<int> and instead just squash both of them down into one so that both Alpha and Beta reference the one canonical implementation of A<int>.

Vulkanologist specialized in pyroclastic flow and Bitcoin mining hardware.

Multiple class declarations are allowed in one translate unit. So it's fine you include class A many times in one .cpp.

But class definition (which define the function code, etc) must be in a single translate unit, except they are inline.

https://www.kbasm.com -- My personal website

https://github.com/wqking/eventpp  eventpp -- C++ library for event dispatcher and callback list

https://github.com/cpgf/cpgf  cpgf library -- free C++ open source library for reflection, serialization, script binding, callbacks, and meta data for OpenGL Box2D, SFML and Irrlicht.

Trying with different take:

When you use something the compiler needs to know the size and names. 

If you want to say a pointer to something, it needs to know it exists.  If you have a function that takes foo(A* const bar) then the compiler needs to know that A is a thing. The compiler already knows how big a pointer is, but it needs to know that an A is a thing.  

If you're working with actual instances of an object the compiler needs to know how big it is and what data it's got.  If an A is 32 bytes, or 64 bytes, or 256 bytes, or some other size, the compiler needs to know how big it is in order to work with them as objects. If you want to access something inside it the compiler needs to know the name and where the data is at inside the structure.

Inline functions can be part of those descriptions when properly marked so the compiler knows they're not the regular standalone functions.

Header files give those details. 

The compiler allows you to repeat those details as many times as you'd like as long as they're the same every time.  It is generally possible to include files multiple times without being an issue other than longer compile times. 

 

However, when you've got functions that manipulate those objects, the actual implementation of the class's functions that code should only appear once. Those are part of a compilation unit and can only exist in one place. When other compilation units call the functions the linker links up the call with the one-and-only implementation.

You're allowed unlimited identical declarations with either internal or external linkage, but only one definition.  Most people create a header that is a class definition, but that's not strictly required.

You can be more strict and remove nearly all the implementation details from both the .h and .cpp files, but it's a significant effort.  In large projects with long build times it can help to make occasional passes to do it, but in general it's not done.

Inclusion guard should prevent cyclic/recursion?

2 hours ago, fleabay said:

Please tell me what you think the purpose of include guards are in header files if there is no problem with multiple declarations of the same class in a translation unit.

I did. Again, note the difference between declaration and definition.

4 hours ago, frob said:

Most people create a header that is a class definition, but that's not strictly required.

 

This topic is closed to new replies.

Advertisement