многократный вызов конструктора глобального объекта
От: andsin  
Дата: 06.07.04 14:09
Оценка:
Привет!
Вопрос, быть может, дурацкий, но как избежать многократного вызова констуктора глобально объекта. В файле cont.h объявлено:
typedef class _CONTAINER{
} CCONTAINER;
// ниже
static CCONTAINER container;

Я думал, что объявив глобальный объект с помощью static, я добьюсь того, что конструктор будет вызван единожды. Ан нет! Конструктор вызывается столько раз, сколько раз в программе встречается #include "cont.h". Как я должен описать объект, чтобы конструктор был вызван однажды?
З.Ы. @pragma once почему-то использовать не хочется.
Спасибо!
Re: многократный вызов конструктора глобального объекта
От: SchweinDeBurg Россия https://zarezky.spb.ru/
Дата: 06.07.04 14:12
Оценка:
Здравствуйте, andsin, Вы писали:

A>Привет!

A>Вопрос, быть может, дурацкий, но как избежать многократного вызова констуктора глобально объекта. В файле cont.h объявлено:
A>
A>typedef class _CONTAINER{
A>} CCONTAINER;
A>// ниже
A>static CCONTAINER container;
A>


здесь
Автор:
Дата: 16.06.04
- Искренне ваш, Поросенок Пафнутий
Re: многократный вызов конструктора глобального объекта
От: Pushkin_С Украина  
Дата: 06.07.04 14:22
Оценка: 14 (1)
Здравствуйте, andsin, Вы писали:

A>Привет!

A>Вопрос, быть может, дурацкий, но как избежать многократного вызова констуктора глобально объекта. В файле cont.h объявлено:
A>
A>typedef class _CONTAINER{
A>} CCONTAINER;
A>// ниже
A>static CCONTAINER container;
A>

A>Я думал, что объявив глобальный объект с помощью static, я добьюсь того, что конструктор будет вызван единожды. Ан нет! Конструктор вызывается столько раз, сколько раз в программе встречается #include "cont.h". Как я должен описать объект, чтобы конструктор был вызван однажды?
A>З.Ы. @pragma once почему-то использовать не хочется.
A>Спасибо!



// В файле file1.cpp
Foo foo;
Foo* f = &foo;
// В файле file2.cpp
extern Foo* f;
Foo f1(*f); // Используется конструктор копий


Если бы все это находилось в одном исходном файле, ситуация была бы нормальной. Со строкой Fоо*
f = &foo; проблем не возникает, поскольку глобальные объекты одного исходного файла заведомо
(хе-хе) инициализируются в порядке их определения. Другими словами, когда программа доберется до
этой строки, объект foo уже будет сконструирован. Тем не менее, никто не гарантирует, что
глобальные объекты в файле file1.cpp будут инициализированы раньше глобальных объектов в файле
file2.срр. Если file2.cpp будет обрабатываться первым, f оказывается равным 0 (NULL на большинстве
компьютеров), и при попытке получить по нему объект ваша программа героически умрет.

Лучший выход — сделать так, чтобы программа не рассчитывала на конкретный порядок
инициализации файлов .срр. Для этого используется стандартный прием — в заголовочном файле .h
определяется глобальный объект со статической переменной, содержащей количество
инициализированных файлов .срр. При переходе от 0 к 1 вызывается функция, которая инициализирует
все глобальные объекты библиотечного файла .срр. При переходе от 1 к 0 все объекты этого файла
уничтожаются.

// В файле Library.h
class Library {
private:
static int count;
static void OpenLibrary();
static void CloseLibrary();
public:
Library();
~Library();
};
static Library LibraryDummy;
inline Library::Library()
{
if (count++ == 0)
OpenLibrary();
}
inline Library::~Library()
{
if (--count == 0)
CloseLibrary();
}
// В Library.cpp
int Library::count = 0; // Делается перед выполнением вычислений
int aGlobal;
40
Foo* aGlobalFoo;
void Library::OpenLibrary()
{
aGlobal = 17;
aGlobalFoo = new Foo;
}
void Library::CloseLibrary()
{
aGlobal = 0;
delete aGlobalFoo;
aGlobalFoo = NULL;
}

К этому нужно привыкнуть. А происходит следующее: файл .h компилируется со множеством других
файлов .срр, один из которых — Library.cpp. Порядок инициализации глобальных объектов,
встречающихся в этих файлах, предсказать невозможно. Тем не менее, каждый из них будет иметь
свою статическую копию LibraryDummy. При каждой инициализации файла .срр, в который включен
файл Library.h, конструктор LibraryDummy увеличивает счетчик. При выходе из main() или при
вызове exit() файлы .срр уничтожают глобальные объекты и уменьшают счетчик в деструкторе
LibraryDummy. Конструктор и деструктор гарантируют, что OpenLibrary() и CloseLibrary()
будут вызваны ровно один раз.

-=Джефф Элджер=- C++ for real programmer
Re: многократный вызов конструктора глобального объекта
От: merlinJap  
Дата: 06.07.04 15:26
Оценка:
Здравствуйте, andsin, Вы писали:

A>Привет!

A>Вопрос, быть может, дурацкий, но как избежать многократного вызова констуктора глобально объекта. В файле cont.h объявлено:
A>
A>typedef class _CONTAINER{
A>} CCONTAINER;
A>// ниже
A>static CCONTAINER container;
A>

A>Я думал, что объявив глобальный объект с помощью static, я добьюсь того, что конструктор будет вызван единожды. Ан нет! Конструктор вызывается столько раз, сколько раз в программе встречается #include "cont.h". Как я должен описать объект, чтобы конструктор был вызван однажды?
A>З.Ы. @pragma once почему-то использовать не хочется.
A>Спасибо!

1. в хидере объявлять переменные нестоит, для этого воспользуйтесь extern;
2. Обязательно заключайте содержимое хидера в:

#ifndef ...
#define ...
#endif


3. Ну а собственно в .сср уже и объявляейте статический объект

Вот пример кода:
Abra.h Он и содержит требуемый объек
#ifndef _CLASSA_H_
#define _CALSSA_H_
...

typedef class CAbra
{
public:
    CAbra(){
        printf("Constructor\n");
    }
    virtual ~CAbra(){}
}ABRA;
#endif

Знаю что коряво объявлять так функции, но совсем не хотелось делать еще один срр
temp1.h Здесь объявляем внешний объект
#ifndef _TEMP1_H_
#define _TEMP1_H_

#include "abra.h"

extern ABRA abra_class;

#endif

temp1.срр Здесь описываем статический объект объявленный внешним
#include "temp1.h"
 static CAbra abra_class;



Вооля...сколько раз не подключай...больше одного раза конструктор не вызоветься (как не проси )
С уважением merlinjap.
Re[2]: многократный вызов конструктора глобального объекта
От: _grisha  
Дата: 06.07.04 17:22
Оценка:
Здравствуйте, merlinJap, Вы писали:

J>temp1.срр Здесь описываем статический объект объявленный внешним

J>
J>#include "temp1.h"
J> static CAbra abra_class;
J>



J>Вооля...сколько раз не подключай...больше одного раза конструктор не вызоветься (как не проси )


Только вот static тут ставить не надо.
Re: многократный вызов конструктора глобального объекта
От: Аноним  
Дата: 06.07.04 18:50
Оценка:
A>Я думал, что объявив глобальный объект с помощью static, я добьюсь того, что конструктор будет вызван единожды.

Как раз наоборот. Объявив его static ты делаешь его локальным, благодаря чему их создается столько, сколько у тебя файлов в которые он включается. А без static тебя бы обругал линкер за множественное определение одноименных переменных.
Re[2]: многократный вызов конструктора глобального объекта
От: vdimas Россия  
Дата: 07.07.04 03:17
Оценка: 2 (2)
Здравствуйте, Pushkin_С, Вы писали:

В этом решении есть одно маленькое "но": а как нам обеспечить некую независимость различных единиц компиляции друг от друга?

вот решение, которым пользуюсь несколько лет:

Initializer.h

    /** Initialization chain */
    class Initializer {
    public:
        static void Init();
        static void Terminate();

    protected:
        Initializer();
        virtual void OnInit() throw() {};
        virtual void OnTerminate() throw() {};

    private:
        Initializer* next;
    };


Initializer.cpp

    //////////////////////////////////////////////////////////////////////////
    // initializer routine
    Initializer* moduleInitializer=0;

    Initializer::Initializer() : next(moduleInitializer) {
        moduleInitializer=this;
    }

    void Initializer::Init() {
        Initializer* i=moduleInitializer;
        while (i) {
            i->OnInit();
            i=i->next;
        }
    }

    void Initializer::Terminate() {
        Initializer* i=moduleInitializer;
        while (i) {
            i->OnTerminate();
            i=i->next;
        }
    }


main.cpp

struct GlobalInitializer {
    GlobalInitializer() { Initializer::Init(); }
    ~GlobalInitializer() { Initializer::Terminate(); }
};

int main() {
    GlobalInitializer globalInitializer;
// start your program here
    
}



надеюсь, суть понятна, использовать крайне просто,
в любой единице трансляции создаете по месту статический екземпляр класса-наследника Initializer, с переопределенными OnInit() и OnTerminate().

-------
когда я это использовал?
тогда, когда конструкторы статических объектов начинают использовать статические экземпляры друг-друга (например — авторегистрация в каком-нить динамическом массиве, а тот, хе-хе, не готов еще...)

так вот, всю подобную логику из конструктора вынести, и в инициализатор внести.

технология крайне гибкая, вплоть до того, что можно по месту создавать класс-гибрид целевого класса и инициализатора:
static struct : public TargetClass, public Initializer {

    [...]

    virtual void OnInit() { 
    // do some initialization...
    // use another static objects from different units
    }

    [...]

} myStaticExemplarOfTargetClass;


успехов.
Re: многократный вызов конструктора глобального объекта
От: andsin  
Дата: 07.07.04 05:16
Оценка:
Большое спасибо! Полная ясность!
Re[2]: многократный вызов конструктора глобального объекта
От: Аноним  
Дата: 07.07.04 12:12
Оценка:
Здравствуйте, andsin, Вы писали:

A>Большое спасибо! Полная ясность!


Остался невыясненный случай, когда один статический объект ссылается на другой. Ок, теперь у нас onInit() ссылается, не конструктор. И точно также ссылается на объект, чей onInit еще не вызвался. Те же яйца только в профиль.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.