tags:

views:

68

answers:

2

I am a bit of a newbie in C++, but I just stumbled on the following.

If I have these files:

myclass.hpp:

class myclass {
public:
  myclass();
  void barf();
};

mymain.cpp:

#include "myclass.hpp"

int main() {
  myclass m;
  m.barf();
  return 0;
}

And I use this implementation of myclass:

myclassA.cpp:

#include <iostream>

using namespace std;

class myclass {   // or include myclass.hpp, it both works fine
public:
  myclass();
  void barf();
};

myclass::myclass() { } //empty
void myclass::barf() {
  cout << "barfing\n";
}

then everything is OK. But if I use this implementation of myclass, which is exactly the same except the members are defined inside the class definition, I get a linking error:

myclassB.cpp:

#include <iostream>

using namespace std;

class myclass {
public:
  myclass() { }
  void barf() {
    cout << "barfing\n";
  }
};

The error I get:

$ g++ myclassB.cpp mymain.cpp
/tmp/cc4DTnDl.o: In function `main':
mymain.cpp:(.text+0xd): undefined reference to `myclass::myclass()'
mymain.cpp:(.text+0x16): undefined reference to `myclass::barf()'
collect2: ld returned 1 exit status

Apparently the object file built from myclassB.cpp doesn't export the member functions. Why aren't both of these implementations behaving te same? Is there some gotcha rule in C++ that says member definitions inside class definitions are not globally visible, but they are if they are defined outside of the class?

+1  A: 

Member functions defined inline are candidates to be inlined by the compiler. If it's never used, and only defined inline inside of a source file, I suspect your compiler is removing the method altogether.

That being said, I highly recommend putting your declaration in a header file, and just have the definitions in your .cpp files. Include the header file (with the appropriate guards, such as #pragma once or #idfef/#define guards. You will need to do this as your projects get larger, so it's best to just build the appropriate practices now.

Reed Copsey
+2  A: 

standard that says that there must be a unique definition of a class, template, etc., is phrased in a somewhat more complicated and subtle manner. This rule is commonly referred to as ‘‘the one definition rule,’’ the ODR. That is, two definitions of a class, template, or inline function are accepted as examples of the same unique definition if and only if [1] they appear in different translation units, and [2] they are tokenfortoken identical, and [3] the meanings of those tokens are the same in both translation units.

For example (valid):

// file1.c:
struct S { int a ; char b ; };
void f(S* p){ }   
// file2.c:
struct S { int a ; char b ; };
void f(S* p){ }  

Examples that violate ODR:

file1.c:
struct S 1 { int a ; char b ; };
struct S 1 { int a ; char b ; }; // error: double definition
This is an error because a struct may not be defined twice in a single 
translation unit.

// file1.c:
struct S 2 { int a ; char b ; };
// file2.c:

struct S 2 { int a ; char b b ; }; // error.
This is an error because S2 is used to name classes that differ in a member name.

// file1.c:
typedef int X ;
struct S 3 { X a ; char b ; };
// file2.c:
typedef char X ;
struct S 3 { X a ; char b ; }; // error.
Here the two definitions of S3 are    
token for token identical, but the example is an error because the
meaning of the name X has sneakily been made to differ in the two files.
Jagannath
To me his question sounded like he actually defined the class only once, but that definition was inside a second class declaration inside his source file.
smerlin
He had myclass.hpp included in mymain.cpp and then trying to compile with g++ myclassB.cpp mymain.cpp.
Jagannath