LNK2019 ссылка на неразрешенный внешний символ Visual Studio 2017 [дубликат]

200
02 февраля 2019, 02:00

На данный вопрос уже ответили:

  • Ссылка на неразрешенный внешний символ (возможные причины) 2 ответа

Уважаемые коллеги, есть у меня основной файл - "test.cpp":

#include "pch.h"
#include <common.h>
#include "String.h" // Здесь определяется мой класс строки
int main()
{
   double d = 12.536;
   double r = common::round(d);
   return 0x0000;
}

Файл "common.h" - это мой собственный заголовочный файл:

#pragma once
#ifndef COMMON_h
#define COMMON_h
namespace common
{
   double round(double val, unsigned precision = 0);
}
#endif

Этот файл лежит где-то на диске D:\, и в компиляторе я в свойствах проектах добавил директорию поиска включаемых файлов. Он его прекрасно находит, но при сборке приложения выдаёт ошибку LNK2019. Рядом с этим файлом в той же директории лежит "common.cpp":

#pragma once
#include <common.h>
double common::round(double val, int precision)
{
   /// Тут какой-то код
   return 0.0;
}

Если я помещаю реализацию метода round() внутрь файла "common.h", то всё работает нормально, но только если я единожды подключаю этот файл. А вот если я его включу и в основной "test.cpp" и в "String.h", то уже выкидывает ошибку LNK2005. Подскажите, пожалуйста, как мне решить эту проблему. Заранее спасибо :)

Answer 1

Теоретическая часть.

Когда компилятор находит определение функции, то он добавляет код функции в объектный файл и экспортирует её имя. Если определить функцию в заголовочном файле, то этот заголовочный файл включается в текст *.cpp файла и будет создан объектный файл с экспортируемой функцией. Если несколько файлов включат этот заголовочный файл себе, то получится, что в нескольких объектных файлах окажутся функции с одинаковыми именами. При линковке объектных файлов линковщик не сможет выбрать нужную функцию, т.к. их будет несколько с одинаковыми именами.

Практическая часть: есть файлы main.cpp, common.h и common.cpp.

Пример 1: функция round() в объявлена в common.h и определена в common.cpp

Лучший вариант. Функция будет создана только в объектном файле common.obj. При линковке вместе main.obj и common.obj будет только одна функция. Всё ОК!

Пример 2: функция round() определена в common.h.

Плохой вариант. Этот заголовочный файл включат main.cpp и common.cpp. Получатся два объектных файла main.obj и common.obj, которые будут иметь в себе определение функции round(). При линковке вместе этих объектных файлов будет ошибка линковки, т.к. линковщик увидит две функции с одинаковыми именами.

Решения:

  • Не определять функции в заголовке, только объявлять их сигнатуры;
  • Определять функции в заголовке, но включать этот заголовок только в один файл с кодом. Данное решение сильно ограничивает в архитектурных решениях;
  • Определять функции в заголовке с модификатором inline. Inline функции не являются так таковыми функциями. Компилятор на месте их вызова вставляет тело функции.
Answer 2

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

namespace common
{
    inline double round(double val, unsigned precision = 0)
    {
        
    }
}
Answer 3

Где лежит ваш common.cpp - "рядом" или "не рядом" - не имеет никакого значения. Вы должны сами явно добавить ваш common.cpp файл в ваш проект, чтобы он компилировался и линковался вместе со всеми остальными файлами программы, т.е. вместе с test.cpp.

Вы этого, очевидно, не сделали. Пока вы этого не сделаете - будет ошибка линковки LNK2019.

Либо так, либо избавляйтесь от common.cpp вообще и реализуйте вашу функцию в common.h как inline.

READ ALSO
Необработанное исключение по адресу 0x00007FF89D0BB985 (ucrtbased.dll)

Необработанное исключение по адресу 0x00007FF89D0BB985 (ucrtbased.dll)

не могу понять , в чём ошибка , пытаюсь просто для начала вывести id из БД oracle

173
Curl возвращает CURLE_LOGIN_DENIED при попытке обращения к mail.ru по IMAP

Curl возвращает CURLE_LOGIN_DENIED при попытке обращения к mail.ru по IMAP

При попытке подключения к imap mailru выплевывает ошибку CURLE_LOGIN_DENIED, пробовал различные комбинации логина (с хостом и без) тот же результат, есть...

222
GWT Как в ListGrid вернуть предыдущую выделенную строку после обработки события?

GWT Как в ListGrid вернуть предыдущую выделенную строку после обработки события?

При клике на другую запись в гриде и имеющихся не сохраненных данных в предыдущей строке бросается предупреждение: перейти без сохранения...

181