Здравствуйте, _agg, Вы писали:
_>Компилятор не выводит никаких сообщений что он игнорирует этот static, собирается это все в Visual Studio 2017.
_>КАК ТАКОЕ ПРОИСХОДИТ ЭТО ЖЕ STATIC? КАК ОН МОЖЕТ СОЗДАВАТЬСЯ МНОЖЕСТВО РАЗ, ВРАЗУМИТЕ КТО ПОНЯЛ В ЧЕМ ДЕЛО, ЧЕГО Я НЕ ВИЖУ ЧТО ПРИВОДИТ К МНОЖЕСТВЕННОМУ СОЗДАНИЮ ОБЪЕКТА СО СПЕЦИФИКАТОРОМ STATIC?
static относительно глобальной переменной ведёт себя иначе по сравнению с переменной в классе или функции. И не гарантирует единичного создания. Он только говорит, что переменная должна быть уникальна для данного файла. В вашем случае в каждом месте, где вы подключаете transfer.h получаете отдельный экземпляр currentClientVersion.
Можете обернуть переменную currentClientVersion в функцию или класс
Utils::VERSION& getCurrentClientVersion()
{
static Utils::VERSION currentClientVersion;
return currentClientVersion;
}
, или объявить extern const Utils::VERSION currentClientVersion в заголовочном и инициализировать в одном из cpp.
Здравствуйте, _agg, Вы писали:
> ...
Если нужно держать какие-то переменные в заголовочном файле, то вместо
static Utils::VERSION currentClientVersion;
можно так (MSVC):
__declspec(selectany) Utils::VERSION currentClientVersion;
__declspec(selectany) гарантирует, что внутри данной единицы компоновки будет только один экземпляр currentClientVersion.
selectany
https://docs.microsoft.com/en-us/cpp/cpp/selectany?view=vs-2019
привет всем, впервые столкнулся с такой "магией" переменной со спецификатором static, можно конечно сделать по другому, но мне хочется понять почему так происходит. Есть DLL в которой множество файлов исходников, есть файлик transfer.h :
#pragma once
#include "Utils.h"
#include <algorithm>
#include <vector>
...
class InfoData
{
public:
...
static InfoData *FromRawData(BYTE * rawData, const DWORD &length);
...
}
static Utils::VERSION currentClientVersion;
есть файлик transfer.cpp:
#include <stdafx.h>
...
const Utils::VERSION checkingClientVersion(19, 6, 3, 0);
...
InfoData *InfoData::FromRawData(BYTE * rawData, const DWORD &length) {
...
if(currentClientVersion >= checkingClientVersion){
...
}else{
...
}
}
сам файлик с классом VERSION Utils.h:
namespace Utils {
...
struct VERSION {
uint32_t versionBytes[4] = { 0,0,0,0 };
CString versionString;
VERSION() {}
VERSION(uint32_t major, uint32_t minor, uint32_t build, uint32_t revision) : versionBytes{ major , minor , build ,revision } {
versionString.Format(_T("%lu,%lu,%lu,%lu"), major, minor, build, revision);
}
~VERSION() {}
bool operator >=(const VERSION &version)const {
return versionBytes[0] >= version.versionBytes[0]
&& versionBytes[1] >= version.versionBytes[1]
&& versionBytes[2] >= version.versionBytes[2];
}
void StringToVersion(const CString &str) { //version expected format: 18,13,124,999-9F8E205
versionString = str;
CString Seperator = _T(".,");
int Position{ 0 }, versPos{ 0 };
CString Token = versionString.Tokenize(Seperator, Position);
versionBytes[versPos++] = _tstoi(Token);
while (!(Token = versionString.Tokenize(Seperator, Position)).IsEmpty()) {
versionBytes[versPos++] = _tstoi(Token);
}
}
};
};
так же есть файлик в котором заполняется клиентская версия Statistic.cpp:
LIBRARY_API DWORD WINAPI GetClientVersion(LPSTR buf, ULONG bufSize) {
...
currentClientVersion.StringToVersion(ver);
...
}
Вся суть этого сводится к сравнению клиентской версии и если она больше либо равна заданной то выполняются определенные действия. В результате получаю такую картину:
ставлю точку останова на конструктор VERSION и смотрю сколько раз создается currentClientVersion получаю адреса создания {7fedc280ff0 ,...еще 4 адреса..., 7fedc2812f8 }
затем заполнение currentClientVersion происходит по адресу 7fedc2812f8
затем сравнение currentClientVersion происходит по адресу 7fedc280ff0
деструкторы срабатывают в обратном порядке создания.
Компилятор не выводит никаких сообщений что он игнорирует этот static, собирается это все в Visual Studio 2017.
КАК ТАКОЕ ПРОИСХОДИТ ЭТО ЖЕ STATIC? КАК ОН МОЖЕТ СОЗДАВАТЬСЯ МНОЖЕСТВО РАЗ, ВРАЗУМИТЕ КТО ПОНЯЛ В ЧЕМ ДЕЛО, ЧЕГО Я НЕ ВИЖУ ЧТО ПРИВОДИТ К МНОЖЕСТВЕННОМУ СОЗДАНИЮ ОБЪЕКТА СО СПЕЦИФИКАТОРОМ STATIC?
Здравствуйте, sergii.p, Вы писали:
Спасибо, век живи век учись.
SP>Здравствуйте, _agg, Вы писали:
_>>Компилятор не выводит никаких сообщений что он игнорирует этот static, собирается это все в Visual Studio 2017.
_>>КАК ТАКОЕ ПРОИСХОДИТ ЭТО ЖЕ STATIC? КАК ОН МОЖЕТ СОЗДАВАТЬСЯ МНОЖЕСТВО РАЗ, ВРАЗУМИТЕ КТО ПОНЯЛ В ЧЕМ ДЕЛО, ЧЕГО Я НЕ ВИЖУ ЧТО ПРИВОДИТ К МНОЖЕСТВЕННОМУ СОЗДАНИЮ ОБЪЕКТА СО СПЕЦИФИКАТОРОМ STATIC?
SP>static относительно глобальной переменной ведёт себя иначе по сравнению с переменной в классе или функции. И не гарантирует единичного создания. Он только говорит, что переменная должна быть уникальна для данного файла. В вашем случае в каждом месте, где вы подключаете transfer.h получаете отдельный экземпляр currentClientVersion.
SP>Можете обернуть переменную currentClientVersion в функцию или класс
SP>SP>Utils::VERSION& getCurrentClientVersion()
SP>{
SP> static Utils::VERSION currentClientVersion;
SP> return currentClientVersion;
SP>}
SP>
SP>, или объявить extern const Utils::VERSION currentClientVersion в заголовочном и инициализировать в одном из cpp.
Здравствуйте, okman, Вы писали:
O>Здравствуйте, _agg, Вы писали:
>> ...
O>Если нужно держать какие-то переменные в заголовочном файле, то вместо
O>O>static Utils::VERSION currentClientVersion;
O>
O>можно так (MSVC):
O>O>__declspec(selectany) Utils::VERSION currentClientVersion;
O>
O>__declspec(selectany) гарантирует, что внутри данной единицы компоновки будет только один экземпляр currentClientVersion.
O>selectany
O>https://docs.microsoft.com/en-us/cpp/cpp/selectany?view=vs-2019
Или (если c++17)
inline Utils::VERSION currentClientVersion;
Имхо inline предпочтительнее, только непонятно зачем оно вообще нужно, если для данного *.h есть сопряженный *.cpp.
По принципу наименьшего удивления лучше объявить обычную extern переменную.