неопределенная ссылка на g в очень простом примере

#c #linker #g #undefined-reference

#c #компоновщик #g #неопределенная ссылка

Вопрос:

Пожалуйста, помогите со следующим вопросом новичка о компиляции и компоновке C и g . По сути, у меня есть 2 класса в 2 разных файлах, и я могу их скомпилировать, но когда я пытаюсь связать, один класс не может видеть методы другого, хотя я связываю оба. Порядок объектных файлов в этом случае не помогает.

Проблема, похоже, связана с конструктором не по умолчанию, который принимает параметр.

Я выделил и воспроизвел проблему в следующем простом коде:

Файл: a.cpp:

 #include <iostream>

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

Файл: a.hpp:

 #ifndef __A_H_
#define __A_H_

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

#endif
  

Файл 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 << "Bn";
    }
};

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

Команды, которые я использую для сборки:

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

Поочередно я пробовал каждый из них:

 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
  

Ошибка, которую я получаю в любом из приведенных выше сценариев ссылок:

 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
  

Заранее спасибо за любую информацию.

(извините, если это повторное сообщение — я думал, что опубликовал его и не вижу его …)

Ответ №1:

Это так не работает. То, с чем вы столкнулись, технически является нарушением ODR, что примерно означает, что A в обоих a.cpp и b.cpp должно быть одно и то же. Это не так.

Более того, конструктор неявно inline находится в a.cpp и, следовательно, его код не обязательно должен быть передан.

Изменение a.cpp на

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

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

исправит ошибку.

Ответ №2:

Вы a.cpp нарушаете правило единого определения и A полностью переопределяете. Вы просто хотите определить функцию в исходном файле:

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

Также вы можете пометить функцию явно, чтобы избежать int того, чтобы s рассматривались как A ‘s в различных нежелательных контекстах.

Ответ №3:

В a.cpp , вы должны #include "a.hpp" , а затем определить конструктор просто как A::A(int i) { ... } . Записывая полное определение class A с помощью кода конструктора в class теле, вы неявно определяете конструктор как встроенную функцию, поэтому в объектном файле для него нет определения.

Ответ №4:

У вас есть два разных класса (один содержит myint , другой нет), оба вызываются class A . Вы не можете этого сделать. Изменить a.cpp на:

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

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

И измените a.hpp на:

 #ifndef __A_H_
#define __A_H_

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

#endif
  

Спасибо за это, как у вас есть, что бы сделал компилятор, если бы кто-то сделал это:

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

Как он может знать, что class A у него есть член, отличный от конструктора? Как он может узнать размер?

Ответ №5:

Вы нарушаете правило единого определения, поскольку в вашей программе есть два отдельных отдельных A класса. Простая обычная реализация вашего A.cpp файл должен выглядеть следующим образом:

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

С «a.h», содержащим правильное определение типа:

 #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;
  

И затем в реализации B вы, вероятно, имели в виду:

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

Ответ №6:

Правильная вещь, которую нужно сделать, это:

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 — остается прежним