Вопрос по linker, c++ – Неразрешенная внешняя символьная статическая переменная (переменная, используемая методом, определенным в заголовке)

4

Вот это .h:

class Logger
{
private:
    static int mTresholdSeverity;

public:
    static __declspec(dllexport) void log(const char* message);
    static __declspec(dllexport) void logFormat(const char* format, ...);

    static __declspec(dllexport) int getTresholdSeverity() { return mTresholdSeverity; }
    static __declspec(dllexport) void setTresholdSeverity(int tresholdSeverity) { mTresholdSeverity = tresholdSeverity; }
};

И .cpp:

#include "Logger.h"
#include <cstdarg>

int Logger::mTresholdSeverity = 200;

void Logger::log(const char* message)
{
    //...
}

void Logger::logFormat(const char* format, ...)
{
    //...
}

Я получаю эту ошибку:
ошибка LNK2001: неразрешенный внешний символ & quot; private: static int TransformationViewer_Utility_Logging :: Logger :: mTresholdSeverity & quot; (? mTresholdSeverity @ Logger @ TransformationViewer_Utility_Logging @@ 0HA) ...

Очевидно, что mTresholdSeverity инициализируется. Ошибка удаляется, если я закомментирую getTresholdSeverity () и setTresholdSeverity () или если я перенесу их определение в файл .cpp.

Почему возникает ошибка ссылки, когда статический метод, определенный в заголовочном файле (getTresholdSeverity () или setTresholdSeverity ()), использует статическую переменную (mTresholdSeverity)?

Вы забыли включить в ссылку объектный файл, полученный в результате компиляции вышеупомянутого cpp? JohnB
@JohnB Нет, я могу использовать метод log (), например, если я получу его для компиляции, выполнив одну из вещей, которые я упомянул в предпоследнем абзаце. Перемещение определения в .cpp является приемлемым решением для меня, мне просто интересно, почему оно не работает, когда определение находится в заголовке. mrzli

Ваш Ответ

3   ответа
0

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

0

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

Невозможно экспортировать или иным образом разделить статическую переменную через границы Windows DLL в процессе. (это возможно для общих объектов Unix, хотя)
Мне кажется что__declspec(dllexport) сделал это; Я только что рассмотрел некоторый код библиотеки Dinkumware (я занимался Windows для этого много лет назад), и это один из механизмов, который там использовался. Это могло измениться совсем недавно.
@PeteBecker Я попытался __declspec (dllexport) для переменной, но ошибка все еще существует, если определения get / setTresholdSeverity () остаются в заголовке. mrzli
Ну, вопрос в том, что он сделал. Причина, по которой невозможно совместно использовать переменную, заключается в том, что адрес переменной в адресном пространстве целевого процесса неизвестен во время сборки или, точнее, во время соединения, когда внешние имена разрешаются в адреса. Системы UNIX выполняют связывание SO во время выполнения, чтобы решить эту проблему.
Ну, ответ в том, что это сработало. Компилятор должен сгенерировать код, который косвенно обращается к переменной через таблицу перемещений, а загрузчик исправляет адрес во время загрузки. Как я и предполагал ранее, это делается с помощью библиотеки времени выполнения Microsoft C ++.
2

Вот как это работает.

Каждая DLL (или EXE) или иным образом завершенная "полностью связанная" двоичный, должен иметь определения всех именованных ссылок, включая статические переменные, в том числе статические члены данных в классах C ++.

Они будут разделены между библиотеками DLL в приложении. Это означает, что значение этой переменной будет различным в зависимости от того, из какой DLL вы смотрите. Перемещение функций в файл CPP заставит их делатьa different thingтеперь они будут видеть копию переменной DLL, а не EXE-файлы.

Чтобы компилировать написанное вами, должно быть определение из CPP, присутствующее во всех пользовательских двоичных файлах в одном месте. Это означает, что DLL и пользователи DLL (EXE или DLL) должны иметь один файл CPP с этим определением.

Это очень большая проблема, потому что среди прочего это делает невозможным использование шаблона Singleton (наличие общего объекта данных для всех пользователей в программе), и каждая копия DLL должна иметь свое собственное статическое состояние. Такая проблема существует только в Windows, с DLL, которые являются динамическими *loaded * библиотеки. В системах UNIX существует другая технология, известная как Shared Objects (SO). Они поддерживают истинное динамическое связывание, что означает запуск компоновщика для разрешения внешних имен во время выполнения.

остальное ложно. отвечая на ваш вопрос ниже.
Поскольку @PavelRadzivilovsky теперь признал, что его информация устарела на двадцать лет, вы можете игнорировать все, что он говорит здесь.
[истинная часть из комментария Пита] Члены статических данных имеют внешнюю связь и определены точно в одном месте в действующей программе. Вы не получаете определения в каждом модуле, в котором он используется; [/ истинная часть из комментария Пита].
Это не верно. Члены статических данных имеют внешнюю связь и определяются ровно в одном месте действительной программы. Вы не получаете определения в каждом модуле, в котором он используется; только в том, который это определяет. Таким образом, во всей программе есть только один, независимо от того, сколько у вас DLL.
Я парень на C #, поэтому я не совсем понимаю этот материал (или все, что вы сказали), но позвольте мне сказать прямо: если у меня есть эти определения методов в заголовке, они не знают, где найти инициализацию / определение для статическая переменная, которую они используют; и если определения методов находятся в файле .cpp, переменная инициализируется в верхней части того же файла, поэтому проблем нет? mrzli

Похожие вопросы