Экземпляр static создается множество раз...
От: _agg  
Дата: 18.07.19 04:30
Оценка:
привет всем, впервые столкнулся с такой "магией" переменной со спецификатором 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?
Отредактировано 18.07.2019 5:07 _agg . Предыдущая версия . Еще …
Отредактировано 18.07.2019 5:01 _agg . Предыдущая версия .
Re: Экземпляр static создается множество раз...
От: sergii.p  
Дата: 18.07.19 05:43
Оценка: +4
Здравствуйте, _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.
Отредактировано 18.07.2019 5:47 sergii.p . Предыдущая версия .
Re[2]: Экземпляр static создается множество раз...
От: _agg  
Дата: 18.07.19 07:12
Оценка:
Здравствуйте, 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.
Re: Экземпляр static создается множество раз...
От: okman Беларусь https://searchinform.ru/
Дата: 18.07.19 07:27
Оценка: 2 (1)
Здравствуйте, _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
Re[2]: Экземпляр static создается множество раз...
От: Chorkov Россия  
Дата: 18.07.19 11:19
Оценка:
Здравствуйте, 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 переменную.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.