Reputation: 13
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
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
Reputation: 208456
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
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
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
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 int
s being treated as A
's in a variety of unwanted contexts.
Upvotes: 1
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