Domanda

Posso controllare l'ordine in cui gli oggetti statici vengono distrutti? C'è un modo per far rispettare il mio ordine desiderato? Ad esempio per specificare in qualche modo che vorrei che un determinato oggetto venisse distrutto per ultimo, o almeno dopo un altro oggetto statico?

È stato utile?

Soluzione

Gli oggetti statici vengono distrutti nell'ordine inverso rispetto alla costruzione. E l'ordine di costruzione è molto difficile da controllare. L'unica cosa di cui puoi essere certo è che due oggetti definiti nella stessa unità di compilazione saranno costruiti nell'ordine di definizione. Qualsiasi altra cosa è più o meno casuale.

Altri suggerimenti

Le altre risposte a questo insistono sul fatto che non può essere fatto. E hanno ragione, secondo le specifiche - ma c'è un trucco che ti permetterà di farlo.

Crea solo una singola variabile statica, di una classe o struttura che contiene tutte le altre cose che normalmente faresti come variabili statiche, in questo modo:

class StaticVariables {
    public:
    StaticVariables(): pvar1(new Var1Type), pvar2(new Var2Type) { };
    ~StaticVariables();

    Var1Type *pvar1;
    Var2Type *pvar2;
};

static StaticVariables svars;

Puoi creare le variabili nell'ordine che ti serve e, soprattutto, distruggere nell'ordine che preferisci, nel costruttore e nel distruttore per StaticVariables. Per renderlo completamente trasparente, puoi anche creare riferimenti statici alle variabili, in questo modo:

static Var1Type &var1(*svars.var1);

Voil # 224 &; - controllo totale. :-) Detto questo, questo è un lavoro extra e generalmente non necessario. Ma quando è necessario, è molto utile conoscerlo.

Risposta breve: in generale no.

Risposta leggermente più lunga: per gli oggetti statici globali in una singola unità di traduzione l'ordine di inizializzazione è dall'alto verso il basso, l'ordine di distruzione è esattamente inverso. L'ordine tra più unità di traduzione non è definito.

Se hai davvero bisogno di un ordine specifico, devi inventarlo da solo.

Gli oggetti statici vengono distrutti nell'ordine inverso rispetto all'ordine in cui sono costruiti (ad es. l'ultimo oggetto costruito viene distrutto per ultimo) e puoi controllare la sequenza in cui sono costruiti gli oggetti statici, usando la tecnica descritta in Articolo 47, & Quot; Assicurati che gli oggetti globali siano inizializzati prima di essere utilizzati & Quot; nel libro di Meyers C ++ efficace .

  

Ad esempio per specificare in qualche modo che vorrei che un determinato oggetto venisse distrutto per ultimo, o almeno dopo un altro onject statico?

Assicurati che sia costruito prima dell'altro oggetto statico.

  

Come posso controllare l'ordine di costruzione? non tutte le statiche sono nella stessa dll.

Ignorerò (per semplicità) il fatto che non si trovano nella stessa DLL.

La mia parafrasi dell'articolo 47 di Meyers (che è lungo 4 pagine) è la seguente. Supponendo che il tuo globale sia definito in un file di intestazione come questo ...

//GlobalA.h
extern GlobalA globalA; //declare a global

... aggiungi un po 'di codice a quel file incluso come questo ...

//GlobalA.h
extern GlobalA globalA; //declare a global
class InitA
{
  static int refCount;
public:
  InitA();
  ~InitA();
};
static InitA initA;

L'effetto di questo sarà che qualsiasi file che include GlobalA.h (ad esempio, il tuo file sorgente GlobalB.cpp che definisce la tua seconda variabile globale) definirà un'istanza statica della classe InitA, che sarà costruita prima di tutto altro in quel file sorgente (ad es. prima della tua seconda variabile globale).

Questa classe InitA ha un contatore di riferimento statico. Quando viene costruita la prima istanza InitA, che ora è garantita prima della costruzione dell'istanza GlobalB, il costruttore InitA può fare tutto ciò che deve fare per assicurarsi che l'istanza globalA sia inizializzata.

Non c'è modo di farlo in C ++ standard, ma se hai una buona conoscenza pratica dei tuoi interni specifici del compilatore, probabilmente può essere raggiunto.

In Visual C ++ i puntatori alle funzioni di init statico si trovano nel segmento .CRT$XI (per init statico di tipo C) o .CRT$XC (per init statico di tipo C ++) Il linker raccoglie tutte le dichiarazioni e le unisce in ordine alfabetico. Puoi controllare l'ordine in cui avviene l'inizializzazione statica dichiarando i tuoi oggetti nel segmento corretto usando

#pragma init_seg

ad esempio, se si desidera creare gli oggetti del file A prima dei file B:

File A.cpp:

#pragma init_seg(".CRT$XCB")
class A{}A;

File B.cpp:

#pragma init_seg(".CRT$XCC")
class B{}B;

.CRT$XCB viene unito prima di .CRT$XCC. Quando il CRT scorre attraverso i puntatori della funzione init statica incontrerà il file A prima del file B.

In Watcom il segmento è XI e le variazioni sull'inizializzazione di #pragma possono controllare la costruzione:

#pragma initialize before library
#pragma initialize after library
#pragma initialize before user

... vedi documentazione per di più

No, non puoi. Non dovresti mai fare affidamento sull'altro di costruzione / distruzione di oggetti statici.

Puoi sempre usare un singleton per controllare l'ordine di costruzione / distruzione delle tue risorse globali.

Hai davvero bisogno che la variabile sia inizializzata prima di main?

In caso contrario, puoi usare un semplice linguaggio per controllare facilmente l'ordine di costruzione e distruzione, vedi qui:

#include <cassert>

class single {
    static single* instance;

public:
    static single& get_instance() {
        assert(instance != 0);
        return *instance;
    }

    single()
    // :  normal constructor here
    {
        assert(instance == 0);
        instance = this;
    }

    ~single() {
        // normal destructor here
        instance = 0;
    }
};
single* single::instance = 0;

int real_main(int argc, char** argv) {
    //real program here...

    //everywhere you need
    single::get_instance();
    return 0;
}

int main(int argc, char** argv) {
    single a;
    // other classes made with the same pattern
    // since they are auto variables the order of construction
    // and destruction is well defined.
    return real_main(argc, argv);
}

Non ti FERMA di provare effettivamente a creare una seconda istanza della classe, ma se lo fai l'asserzione fallirà. Nella mia esperienza funziona benissimo.

Puoi ottenere effettivamente funzionalità simili avendo un static std::optional<T> invece di un T. Inizializzalo come faresti con una variabile, usalo con l'indirizzamento indiretto e distruggilo assegnando std::nullopt (o, per potenziare, boost::none).

È diverso dall'avere un puntatore in quanto ha una memoria preallocata, il che è quello che immagino tu voglia. Pertanto, se lo distruggi & Amp; (forse molto più tardi) ricrearlo, il tuo oggetto avrà lo stesso indirizzo (che puoi conservare) e in quel momento non paghi il costo di allocazione / deallocazione dinamica.

Usa boost::optional<T> se non hai std:: / std::experimental::.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top