[c++] неинициализированные статические переменные

doublemother

Что говорит на их счёт стандарт?
class A {
public:
   char *aaa;
   static char *bbb;
}

Если я правильно помню, aaa в зависимости от компилятора может оказаться как нулём, так и нет. А вот будет ли нулём bbb и всегда ли? Сам никаких упоминаний в стандарте, к сожалению, не нашёл.

Andbar

Если я правильно помню, aaa в зависимости от компилятора может оказаться как нулём, так и нет. А вот будет ли нулём bbb и всегда ли? Сам никаких упоминаний в стандарте, к сожалению, не нашёл.
У тебя bbb только задекларировано, но не определено. Оно должно быть определено в некоторой одной единице компиляции, иначе будет ошибка линковки.
Если же ты определишь bbb без инициализации, то будет тоже самое, что с любыми другими глобальными переменными без инициализации, в частности для классов будет конструктор по умолчанию вызван.
А почему aaa должен быть нулём заполняться, я не понял: если обнуление не прописано в конструкторе, то зачем совершать лишнее действие, если лучше выдать хинт/варн?
upd:
9.4.2 Static data members
...
2 The declaration of a static data member in its class definition is not a definition and may be of an
incomplete type other than cv-qualified void. The definition for a static data member shall appear in a
namespace scope enclosing the member’s class definition. In the definition at namespace scope, the name
of the static data member shall be qualified by its class name using the :: operator. The initializer
expression in the definition of a static data member is in the scope of its class (3.3.6).
...
7 Static data members are initialized and destroyed exactly like non-local objects (3.6.2, 3.6.3).

doublemother

Определено на самом деле не у меня и суть кода такая: имеется хидер, в котором описывается синглтон. Соответственно возможное описание примерно такое:
class A {
private:
A;
static A* a;
public:
A& instance {
if(!a) a = new A;
return a;
}

Сразу написать "static A* a = 0;" нельзя, компилятор пошлёт далеко и надолго.
Сейчас это реализуется как
class A {
private:
A;
public:
A& instance {
static A a;
return a;
}

Но хотелось бы понять, как это всё-таки сделать через переменную класса. Поскольку это хидер, вынести инициализацию переменной за пределы класса нельзя.

doublemother

А почему aaa должен быть нулём заполняться, я не понял: если обнуление не прописано в конструкторе, то зачем совершать лишнее действие, если лучше выдать хинт/варн?
Варн, естественно, выдаётся, тем не менее указатель в gcc по умолчанию вроде бы всё-таки равен нулю.

Dasar

Сейчас это реализуется как
и чем не устраивает? это рекомендуемое стандартное решение - когда static переменная класса заменяется на static переменную внутри метода.

doublemother

Всем устраивает, просто стало интересно.

Andbar

Я синглтон описываю через шаблонный класс:
template<class _Sy>
class Singleton
{
static std::auto_ptr<_Sy> instance;
Singleton(Singleton&) {}
protected:
Singleton {}
~Singleton {}
public:
static _Sy &GetInstance(void);
};

template<typename _Sy> std::auto_ptr<_Sy> Singleton<_Sy>::instance;

template<class _Sy>
_Sy &Singleton<_Sy>::GetInstance(void)
{
if (!instance.get
{
instance.reset(new _Sy;
}
return static_cast<_Sy&>(*instance);
}
Для шаблонных классов стандарт разрешает определение статических членов в заголовке. Минимальный код, необходимый для делания класса A синглтоном следующий:
class A : public Singleton<A>
{
private:
A {}
~A {}
friend class Singleton<A>;
friend class std::auto_ptr<A>;
};

Serab

И это все только чтобы не оставлять переменную экземпляра как локальную в функции?

doublemother

Зато решение интересное, кстати.

Serab

Зато решение интересное, кстати.
Ты даже не понимаешь, где определение, а где объявление, что ты можешь сказать об интересности решения?
Я к тому, что наворотить можно много чего «интересного», понимать бы зачем.
Если auto_ptr «потокобезопасен», то я солидарен с этим решением, но ты ведь не об этом подумал.
upd: да даже если дело в многопоточности, то InterlockedCompareExchange решает проблему, правда с переносимостью вылазит проблема.
В общем, , поясни, не догоняю.

Andbar

Приведённый мною код не годится для многопоточных приложений, но тоже самое можно сказать и про вариант со статической переменной в методе instance (достаточно посмотреть на ассемблерный код).
Но мой код гораздо легче сделать потокобезопасным, собственно, изначально я написал именно такую версию (почти рабочую: позже выяснилось, что m$ нагло врёт в документации касательно слова volatile а затем уже удалил всё, что касается защиты от одновременного вызова из двух потоков.
upd: да даже если дело в многопоточности, то InterlockedCompareExchange решает проблему, правда с переносимостью вылазит проблема.
Что-то не приходит в голову достаточно красивый и рабочий вариант решения проблемы. С критической секцией (а её обёртку в класс не сложно перенести) гораздо проще выходит.

Serab

Что-то не приходит в голову достаточно красивый и рабочий вариант решения проблемы. С критической секцией (а её обёртку в класс не сложно перенести) гораздо проще выходит.
Критическая секция неоправданно медленнее.

Serab

m$ нагло врёт в документации касательно слова volatile
Пруфлинк.
Могу посоветовать прочитать мнение Андрея Александреску.

Andbar

Критическая секция неоправданно медленнее.
давай ты сперва напишешь свой вариант (используя статическую переменную я свой, а затем будем сравнивать

Serab

Да, согласен, твой код подходит как отправная точка для установления потоковой безопасности и в нем обошли проблему описания в заголовочном файле. В такой формулировке видна ценность этого кода.

Serab

давай ты сперва напишешь свой вариант (используя статическую переменную я свой, а затем будем сравнивать
InterlockedCompareExchange против критической секции, а не против базового шаблона для синглтонов.

Andbar

уже не помню: нашёл на их сайте какой-то doc-файл, описывающий синхронизацию при написании драйверов, там было указано, что новые студии вставляют aquire перед чтением и release после записи volatile-переменных, только на практике это не подтвердилось: для работы какого-то теста пришлось вручную добавлять MemoryBarrier, т.е. одного volatile'а оказалось недостаточно.
А, ещё в мсдн за 2008й год указано, что начиная с 2005й студии так делается.

Serab

Подумал, конструктор все равно синхронизировать придется, Interlocked* не поможет, придется критическую секцию делать.

Serab

вставляют aquire перед чтением и release после записи volatile-переменных
Там сказано про Acquire _semantics_ и Release _semantics_, в том же MSDN подробно разъяснено, что под этим имеется в виду. Никто не обещал поназаводить критических секций.
Acquire memory semantics specify that the memory operation being performed by the current thread will be visible before any other memory operations are attempted. Release memory semantics specify that the memory operation being performed by the current thread will be visible after all other memory operations have been completed.

Andbar

ок, убедил: я недочитал мсдн. Впрочем, ошибки там всё равно попадаются, во всяком случае раньше были (в онлайн-версии пока не находил, т.к. редко ею пользуюсь, но в локальной версии за 2005й год пару раз натыкался на то, что опровергалось на практике).

Serab

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

doublemother

А что я еще не понимаю? Расскажи, мне правда интересно.
О многопоточности в данном случае речи не шло.

slonishka

оно же в bss ляжет.
bss не зануляется под вендой?

Serab

А что я еще не понимаю? Расскажи, мне правда интересно.
Кто ж тебя знает, что ты еще не понимаешь.
Вот из этого
О многопоточности в данном случае речи не шло.
следует, что ты не понял, зачем написал такую хитрую реализацию. Хотя ты назвал ее "интересной". Если не брать в рассчет многопоточность, то называть ее "интересной" как минимум глупо. В случае заведомо однопоточного приложения (что в наши дни редкость, но все же есть еще ситуации, когда можно считать приложение однопоточным) это решение я бы назвал "избыточным".

doublemother

Избыточное, да, я не спорю, и в моем случае вариант с локальной static переменной куда удобнее. Но что мешает его реализации при этом быть интересной? Не красивой, удобной или короткой, а просто интересной?

doublemother

Во, вот об этом я не подумал. Вроде как bss зануляется всегда.

Serab

Во, вот об этом я не подумал. Вроде как bss зануляется всегда.
Не всегда, а по умолчанию.

valodyr

А как сделать не по умолчанию? C runtime подменить?

Serab

А как сделать не по умолчанию?
Задать значение при описании, вестимо

// global scope:
int i = 7;

или

void whatever
{
static int j = 18;
...
}

valodyr

Всё чудесно, только тогда это попадёт в data, а не в bss.

Serab

А хотя я спизднул. bss - это именно для нулевых. Читать не умею, видимо :crazy:
Оставить комментарий
Имя или ник:
Комментарий: