ошибка LNK2005: уже определено — C

#c #linker

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

Вопрос:

Предыстория

У меня есть проект с именем PersonLibrary, который содержит два файла.

  1. Person.h
  2. 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. Почему показанный первым код работает не так, как я ожидал?
  2. Какая статическая разница здесь?
  3. Каково приблизительное решение этой проблемы?

Спасибо

Ответ №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:

  1. Функция SetPersonName будет скомпилирована в каждый объектный файл, который включает файл Person.h, таким образом, компоновщик увидит несколько функций и выдаст ошибку.

  2. При написании static вы указываете, что функция будет видна только в одном файле objectfile. Вы все равно получите несколько функций в своем двоичном файле, но теперь вы не получите ошибок.

  3. Попробуйте написать 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;