#c #linker
#c #компоновщик
Вопрос:
Предыстория
У меня есть проект с именем PersonLibrary, который содержит два файла.
- Person.h
- Person.cpp
Эта библиотека создает файл статической библиотеки. Другим проектом является TestProject, который использует PersonLibrary (добавлен через зависимости проекта в VS008). Все работало нормально, пока я не добавил функцию, не являющуюся членом, в Person.h. Person.h выглядит как
class Person
{
public:
void SetName(const std::string name);
private:
std::string personName_;
};
void SetPersonName(Personamp; person,const std::string name)
{
person.SetName(name);
}
Person.cpp определяет функцию setName. Когда я пытаюсь использовать SetPersonName из TestProject, я получаю ошибку LNK2005: уже определено. Вот как я это использовал
#include "../PersonLibrary/Person.h"
int main(int argc, char* argv[])
{
Person person;
SetPersonName(person, "Bill");
return 0;
}
Попробованы обходные пути
1 — Я удалил Person.cpp и определил весь класс в Person.h. Ошибка исчезла, и все заработало.
2 — Изменен модификатор SetPersonName на статический. Как показано ниже
static void SetPersonName(Personamp; person,const std::string name)
{
person.SetName(name);
}
Вопросы
- Почему показанный первым код работает не так, как я ожидал?
- Какая статическая разница здесь?
- Каково приблизительное решение этой проблемы?
Спасибо
Ответ №1:
Вы либо должны
- переместите
SetPersonName
определение в файл .cpp, скомпилируйте и свяжите с результирующей целью - сделать
SetPersonName
встроенным
Это хорошо известный случай нарушения одного правила определения.
Ключевое слово static делает привязку функции внутренней, т. е. доступной только для единицы перевода, в которую она включена. Это, однако, скрывает реальную проблему. Я бы предложил переместить определение функции в ее собственный файл реализации, но сохранить объявление в заголовке.
Комментарии:
1. Спасибо за ответ. Я разобрал это.
2. @dirkgently… но boost — это IIRC только для заголовка, возможно, и другие библиотеки тоже. Как они избегают таких проблем?
3. Кое-что, что может сэкономить время другим: в случае, если вы компилируете программу C на VS, вы должны использовать __inline, чтобы сделать ее встроенной (иначе при компиляции произойдет сбой)
4. Я тоже сталкиваюсь с проблемой с глобальными переменными по точно такому же сценарию, как здесь. Просто замените SetPersonName() глобальной переменной. Как бы я решил это?
Ответ №2:
Когда вы компилируете свою библиотеку, ее lib-файл содержит определение для SetPersonName. Когда вы компилируете свою программу, использующую библиотеку, поскольку она включает заголовок, и вы написали код, встроенный в заголовок, он также компилируется в определение для SetPersonName . Два определения для одной и той же функции (обычно) не допускаются. Ключевое слово static сообщает компилятору, что функция не должна быть доступна за пределами текущей единицы преобразования (отдельного фрагмента кода, который вы компилируете), поэтому определение в библиотеке не видно компоновщику.
Подходящее решение этой проблемы зависит от ваших целей. Заголовочные файлы со статическими объявлениями функций почти никогда не являются тем, что вы хотите. С точки зрения дизайна я бы рекомендовал вообще избавиться от SetPersonName и просто использовать Person::setName .
Однако, в противном случае я бы реализовал это так же, как вы сделали для остальной части вашей функциональности, объявлений в заголовке и реализации в .cpp. Встроенные функции, связанные с библиотекой, будут иметь тенденцию уменьшать многие преимущества использования библиотеки в первую очередь.
Ответ №3:
Объявляя функцию static, вы ограничиваете ее область действия текущей единицей перевода, поэтому фактически вы добавили новую функцию SetPersonName в свой основной файл и вызывали бы ее, а не ту, которая определена в библиотеке.
Правильное решение — объявить SetPersonName как extern лично.h и реализовать это в person.cpp
Person.h
extern void SetPersonName(Personamp; person,const std::string name);
Person.cpp
void SetPersonName(Personamp; person,const std::string name)
{
person.SetName(name);
}
Ответ №4:
-
Функция SetPersonName будет скомпилирована в каждый объектный файл, который включает файл Person.h, таким образом, компоновщик увидит несколько функций и выдаст ошибку.
-
При написании static вы указываете, что функция будет видна только в одном файле objectfile. Вы все равно получите несколько функций в своем двоичном файле, но теперь вы не получите ошибок.
-
Попробуйте написать
inline
перед функцией, подобнойinline void SetPersonName(Personamp; person,const std::string name) { person.SetName(name); }
… поскольку функция довольно простая, я думаю, что нормально использовать ее как встроенную. Встроенный файл поместит необходимый код туда, где используется функция, без фактического создания вызываемой функции.
Ответ №5:
Решением было бы сделать эту функцию статическим методом. Это остановит «уже определенные» ошибки.
Ответ №6:
У меня была похожая ситуация, четко описанная @logan-capaldo выше.
Исходный файл CPP (myfile.cpp ) содержал функцию myFunction. При сборке это было скомпилировано в myfile.obj. Но основной CPP-файл (main.cpp ) также включено myfile.cpp таким образом, функция myFunction была включена / скомпилирована / связана дважды, что привело к ошибке «LNK2005 уже определено».
Это беспорядочно, но у меня не было времени исправить это должным образом. Самым быстрым решением (в VS Express 2012) было щелкнуть правой кнопкой мыши myfile.cpp в обозревателе решений перейдите в Свойства и измените Исключено из сборки на Да. Я предполагаю, что это предотвращает создание и / или связывание одного из OBJ-файлов и, таким образом, устраняет ошибку.
Ответ №7:
Для всех, кто попадает сюда, имея дело с этой ошибкой в проекте Qt, убедитесь, что у вас нет никаких не сигнальных функций, определенных в signals:
ваших файлах заголовков.
Неверно, выдает LNK2005
на Foo::promiseData()
:
class Foo : public QObject {
Q_OBJECT
public:
explicit Foo(QObject* parent = nullptr);
signals:
void dataReady(QList<QObject*> data) const;
void promiseData() const; // <-- This function is not supposed to be a signal.
Исправить:
class Foo : public QObject {
Q_OBJECT
public:
explicit Foo(QObject* parent = nullptr);
void promiseData() const;
signals:
void dataReady(QList<QObject*> data) const;