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