Re: classes, strings, learning in VS.NET

From: Peter van Merkerk (merkerk_at_deadspam.com)
Date: 12/02/03


Date: Tue, 2 Dec 2003 14:36:18 +0100


> ok it works, thx!!
>
> now another problem:
>
> I've got a file X.cpp and an A.cpp and B.cpp
> X is the base class and class A and class B are derived from class X
(class
> A: public X)
> so in A.cpp and B.cpp I write: #include "X.cpp"
> then in my main.cpp I write #include "A.cpp" and #include "B.cpp"
> and trouble strikes: X: class type redefinition

I will discuss a technique to avoid redefinition errors at the end of
this posting.

But first of all you should not include .cpp files!

Secondly, in C++ it is common to separate the class definition and the
class implementation. Though the Java way of merging the class
definition and implementation into one file is possible in C++, it is
not recommended way of doing things.

The class definition usually goes into a header file with a .h
extension. In some environments a different extension is used for header
files, but on VS the .h extension is the most common.

A class definition looks something like this:

--------- X.h ---------
class X
{
   public:
        virtual void foo();
};

The class implementation goes into the .cpp file. These files are
officially called translation units. Translation units serve as input
for C++ compiler. The compiler processes only one translation unit at a
time. Even though your project (or makefile) may consist of many .cpp
files, it is important to understand that the compilation process of the
.cpp files is completely independent from each other. The fact that .cpp
files are compiled separately is nowadays hidden by modern IDE's,
nevertheless it is still important to understand this aspect of the C++
compilation process.

If you want to use a class, derive from it or implement its member
functions, the class definition (that what is stored in the .h file)
must be known to the compiler.

The class implementation file of the X class could look something like
this:

--------- X.cpp ---------
#include "X.h"
#include <iostream>

void X::foo()
{
    std::cout << "void X::foo() called" << std::endl;
}

The compiler does not need the class implementation to be able to call
functions on that class or to derive from it. This is the reason why you
should include .h files instead of .cpp files.

Example:
--------- main.cpp ---------
#include "X.h"

int main()
{
    X x;
    x.foo();
    return 0;
}

When compiling the main.cpp file the preprocessor will substitute the
line #include "X.h" with the contents of the X.h file. This way the
compiler can "see" the definition of the X class when compiling the
main.cpp file. When main.cpp is compiled, the compiled code goes into
the main.obj, the compiled code references a X::foo() function, but at
this stage that reference is not resolved. After the X.cpp file is
compiled the compiled code for the X::foo() function can be found in the
X.obj file. The linker puts the relevant code from main.obj and X.obj
into the executable file and resolves the call main() makes to X::foo().

(note that the linking process and .obj file extensions are VS specific
and are beyond the scope of the C++ standard)

To define a derived class the definition of the base class is also
needed:

--------- A.h ---------
#include "X.h"
class A : public X
{
   public:
        virtual void foo();
};

The implementation file of class A only has to include A.h because the
definition of class X will be include via the A.h file:

--------- A.cpp ---------
#include "A.h"
#include <iostream>

void A::foo()
{
    std::cout << "void A::foo() called" << std::endl;
}

A common problem with include files is that directly or indirectly the
include file is included twice within the same translation unit.
Compilers don't like this because it means that they will see
definitions twice or more; there you have your "redefinition error". For
example in the example below X.h is included directly and indirectly via
A.h

--------- main.cpp ---------
#include "X.h"
#include "A.h"

int main()
{
    X x;
    x.foo();
    return 0;
}

A common solution for this problem is putting include guards in the .h
files:

--------- X.h ---------
#ifndef X_H
#define X_H

class X
{
   public:
        virtual void foo();
};

#endif

When the X.h file is seen once inside a translation unit the next time
it gets included it will skip the definitions and declarations inside
the X.h file. Most experienced C++ programmers will always put include
guards in header files, even when leaving those out would not cause
redefinitions errors at that point in time.

--
Peter van Merkerk
peter.van.merkerk(at)dse.nl


Relevant Pages

  • Re: What a translation unit is.
    ... >> compiler proper. ... >> a translated translation unit come out of the compiler proper. ... >say an object file or an object-code library or whatever. ...
    (alt.comp.lang.learn.c-cpp)
  • Re: What a translation unit is.
    ... >> Now you can use that compiler output as input to your linker. ... it's why all the people who are quoting standards are ... in which "translation unit" has a very well-defined meaning ...
    (alt.comp.lang.learn.c-cpp)
  • Re: Summary: translation units, preprocessing, compiling and linking?
    ... > converted into a translation unit, then object code, and then linked. ... Each translation unit is converted into one object file. ... When the compiler gets its hands on 'ab.cc' what does it see? ...
    (comp.lang.cpp)
  • Re: What a translation unit is.
    ... >>There are plenty of quotes all over the net that describe translation ... >>passed to the linker and that a translation unit is and object file. ... >>compiler obviously. ...
    (alt.comp.lang.learn.c-cpp)
  • Re: What a translation unit is.
    ... >> other quotes from MSDN contradict). ... >> other MSDN quotes, and quotes from Borland and Comeau docs. ... >translation unit can ONLY be used as an entity that is input to a compiler ...
    (alt.comp.lang.learn.c-cpp)