#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
и определить, почему эти две функции реплицируются. Или, может быть, вы не должны связывать как фиктивные, так и реальные функции в одной программе — два исходных файла просто не предназначены для соединения в одной программе.