fbg00
fbg00

Reputation: 13

g++ undefined reference in very simple example

Please help with the following noob question about C++ and g++ compilation and linking. Essentially I have 2 classes in 2 different files, and can compile them but when I attempt to link, one class can't see the methods of the other, even though I am linking both. Order of object files does not help in this case.

The problem seems related to a non-default constructor that takes a parameter.

I have distilled and reproduced the problem in the following simple code:

File: a.cpp:

#include <iostream>

class A
{
  public:
  int my_int;
    A(int i) {
      my_int = i;
      std::cout << "A";
    }
};

File: a.hpp:

#ifndef __A_H_
#define __A_H_

class A
{
  public:
  A(int i);
};

#endif

File b.cpp:

#include <iostream>

using namespace std;

#include <a.hpp>

class B
{
  public:
  int my_int;
    B(int i) {
      my_int = i;
      A a(i);
      cout << "B\n";
    }
};

int main(int argc, char* argv[])
{
  B b(5);
  cout << "hello world: ";
  cout.flush();
  return 0;
}

Commands I use to build:

g++ -c -I. a.cpp
g++ -c -I. b.cpp
g++ -o c_test a.o b.o

Alternately, I've tried each of these:

g++ -o c_test b.o a.o
g++ -I. -o c_test a.cpp b.cpp
g++ -I. -o c_test b.cpp a.cpp

Error I get in any of above link scenarios:

b.o: In function `B::B(int)':
b.cpp:(.text._ZN1BC1Ei[B::B(int)]+0x1c): undefined reference to `A::A(int)'
collect2: ld returned 1 exit status

Thanks in advance for any insight.

(sorry if this is a re-post -- I thought I posted it and don't see it...)

Upvotes: 1

Views: 2679

Answers (6)

weekens
weekens

Reputation: 8292

The right thing to do is:

a.hpp

#ifndef __A_H_
#define __A_H_

class A
{
  public:
    int my_int;
  A(int i);
};

#endif

a.cpp

#include <iostream>

#include "a.hpp"

A::A(int i) {
      my_int = i;
      std::cout << "A";
    }

b.cpp - remains the same

Upvotes: 0

You are breaking the One Definition Rule, as you have two distinct separate A classes in your program. The simple common implementation of your A.cpp file should look like:

#include "a.h" // where the definition of the type is
A::A( int x ) : myint(i) {}

With "a.h" containing the proper definition of the type:

#ifndef A_H_ // identifiers containing double underscores (__) are reserved, don't use them
#define A_H_
class A
{
  int myint; // This must be present in the definition of the class!
public:
  A(int i);
};
#endif;

And then in the implementation of B, you probably meant:

#include "a.h"     
#include <iostream>

using namespace std;  // I don't like `using`, but if you use it, do it after all includes!
class B {
public:
//   int my_int;      // do you really want to hide A::my_int??
    B(int i) : A(i) { // use the initializer list
      cout << "B\n";
    }
};

Upvotes: 0

David Schwartz
David Schwartz

Reputation: 182883

You have two different classes (one containing myint, one not) both called class A. You can't do that. Change a.cpp to:

#include <iostream>
#include "a.hpp"

A::A(int i) {
  my_int = i;
  std::cout << "A";
}

And change a.hpp to:

#ifndef __A_H_
#define __A_H_

class A
{
 public:
 int my_int;
 A(int i);
};

#endif

Thank about it, the way you have it, what would the compiler do if someone did this:

#include "a.hpp"
// ...
    A foo(3);
    cout << sizeof(foo) << endl;

How could it know that class A has a member other than the constructor? How could it know the size?

Upvotes: 0

Wyzard
Wyzard

Reputation: 34591

In a.cpp, you should #include "a.hpp" and then define the constructor simply as A::A(int i) { ... }. By writing a whole definition of class A with the constructor code within the class body, you're implicitly defining the constructor as an inline function, which is why there's no definition for it in the object file.

Upvotes: 0

Mark B
Mark B

Reputation: 96311

You a.cpp is violating the one definition rule and redefining A entirely. You just want to define the function in your source file:

A::A(int i) {
  my_int = i;
  std::cout << "A";
}

Also you may want to mark the function explicit to avoid ints being treated as A's in a variety of unwanted contexts.

Upvotes: 1

jpalecek
jpalecek

Reputation: 47770

It doesn't work that way. What you've come across is technically an ODR violation, which roughly means that A in both a.cpp and b.cpp must be the same thing. It isn't.

Moreover, the constructor is implicitly inline in a.cpp and therefore its code needn't be emitted.

Changing a.cpp to

#include <iostream>
#include "a.hpp"

A::A(int i) {
      my_int = i;
      std::cout << "A";
}

will fix the error.

Upvotes: 3

Related Questions