Как найти несколько определений функции

#c #qt #linker

#c #qt #компоновщик

Вопрос:

Я написал FindDialog, который находит искомый текст. Когда я даю make команду, она возвращает

 g   -Wl,-O1 -o findDialog FindDialog.o main.o moc_FindDialog.o    -L/usr/lib -lQtGui -lQtCore -lpthread 
moc_FindDialog.o: In function `FindDialog::findClicked()':
moc_FindDialog.cpp:(.text 0x20): multiple definition of `FindDialog::findClicked()'
FindDialog.o:FindDialog.cpp:(.text 0x30): first defined here
moc_FindDialog.o: In function `FindDialog::enableFindButton(QString constamp;)':
moc_FindDialog.cpp:(.text 0x50): multiple definition of `FindDialog::enableFindButton(QString constamp;)'
FindDialog.o:FindDialog.cpp:(.text 0x0): first defined here
collect2: ld returned 1 exit status
make: *** [findDialog] Error 1
  

Я искал проблему в течение нескольких часов, но я не могу понять, из-за чего возникает проблема.
Что может вызвать multiple definition of ошибку?

Комментарии:

1. Не могли бы вы предоставить какие-либо исходные коды?

Ответ №1:

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

Иметь заголовочный файл header.hpp как с объявлением метода, так и с его определением:

 class foo {
public:
  void bar ();
};

void foo::bar ()
{
}
  

И иметь два исходных файла source1.cpp и source2.cpp оба включают этот файл:

source1.cpp :

 #include "header1.hpp"
int example1()
{
  foo f;
  f.bar ();
}
  

… и source2.cpp :

 #include "header1.hpp"
int main ()
{
  foo f;
  f.bar ();
  f.bar ();
}
  

Затем скомпилируйте два файла по отдельности и свяжите их вместе. Например:

 g   -c source1.cpp source1.o
g   -c source2.cpp source2.o
g   -o a.out source1.o source2.o
  

Это приведет к ошибке компоновщика, которую вы описали в своем вопросе, потому что метод foo::bar появляется дважды, как в объектах source1, так и в объектах source2. Компоновщик не знает, какое из них использовать.

Существует два распространенных решения этой проблемы:

Решение № 1 — Встроить этот метод.

Объявив этот метод с inline ключевым словом, компилятор либо встроит весь метод целиком, либо, если решит этого не делать, сгенерирует анонимный метод (тот же метод, но с некоторым уникальным именем для данного объектного файла), так что конфликтов в объектных файлах не будет. Например:

 class foo {
public:
  void bar ();
};

inline void foo::bar ()
{
}
  

Решение № 2 — Иметь определение (реализацию) этого метода в другом исходном файле, чтобы он появлялся только один раз во всей программе. Например:

header1.hpp :

 class foo {
public:
  void bar ();
};
  

header1.cpp :

 #include "header1.hpp"
void foo::bar ()
{
}
  

Чтобы решить, встроить или нет, вы должны знать (или, по крайней мере, сделать предположение), является ли вызов этой функции более дорогостоящим, чем дублирование / встраивание этого кода по всей программе. Встроенный код обычно увеличивает объем вашей программы и увеличивает время компиляции. Но это не обязательно ускоряет работу. Кроме того, наличие определения в исходном файле не приведет к повторной компиляции всех исходных файлов с использованием этой функции, а только к повторной компиляции исходного файла, в котором есть определение, а затем к повторной компоновке. Многие программисты сходят с ума по поводу встраивания C , на самом деле не понимая, как это влияет на программу. Я бы рекомендовал использовать определение в исходном файле и делать его встроенным, только если вызов этой функции становится узким местом в производительности, а в противном случае ее встроенность исправит это.

Надеюсь, это поможет. Удачного кодирования!

Комментарии:

1. Спасибо за ответ. Причина в том, что автоматически генерируется moc_FindDialog.cpp включает функции, которые я написал в своем FindDialog.cpp файлы. Когда я удалил эти функции, это сработало. Но я не понимаю одной вещи, почему Qt пишет эти функции, которые я уже написал.

2. Хм, я действительно не знаю, почему исходный код, сгенерированный moc, включает в себя ваш другой исходный файл. Возможно, вы пытаетесь переопределить слоты, а не соединять их, или что-то в этом роде.

Ответ №2:

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

Пишите в заголовках только следующее:

  • Определения классов
    • Может включать встроенные определения для функций-членов
  • Объявления переменных, не являющихся членами
  • Объявления функций, не являющихся членами
  • Определения шаблонов / встроенных функций

Поместите все остальное в «исходные файлы».

Комментарии:

1. Да, вы правы насчет защиты заголовка, которые широко рекомендуется использовать для решения проблемы. Однако они не помогли.

2. @stedkka: Защита заголовка решает немного другую проблему (которая, говоря в самом общем виде, заключается в множественном объявлении , а не в определении ).

Ответ №3:

Ответ Влада Лазаренко хорош.

Я просто добавляю еще одну возможность, с которой я столкнулся при использовании QT / C :

Когда вы объявляете слот, вам не нужно писать определение (реализацию), в противном случае вы получите сообщение об ошибке: множественное определение

Ответ №4:

Судя по именам файлов в сообщениях об ошибках, у вас есть несколько макетных тестовых приспособлений как часть TDD и некоторый реальный код. Компоновщик довольно четко сообщил, в чем проблема — я переформатировал информацию здесь для дополнительной ясности, удалив часть информации:

 moc_FindDialog.cpp: multiple definition of `FindDialog::findClicked()'
FindDialog.cpp:     first defined here

moc_FindDialog.cpp: multiple definition of `FindDialog::enableFindButton(QString constamp;)'
FindDialog.cpp:     first defined here
  

Здесь четко указано, что компоновщик сначала столкнулся с каждым из определений функции в moc_FindDialog.cpp , а затем столкнулся со вторым определением в FindDialog.cpp .

Я думаю, вам нужно внимательно изучить макет тестового устройства в moc_FindDialog.cpp и определить, почему эти две функции реплицируются. Или, может быть, вы не должны связывать как фиктивные, так и реальные функции в одной программе — два исходных файла просто не предназначены для соединения в одной программе.