开发者

g++ undefined reference in very simple example

开发者 https://www.devze.com 2023-04-10 00:08 出处:网络
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

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.cp开发者_C百科p 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...)


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.


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.


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.


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?


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";
    }
};


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

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号