2015-02-24 17 views
5
#include <iostream> 
struct A 
{ 
    A(){std::cout<<"A()"<<std::endl;} 
}; 

template<typename T> 
struct B 
{ 
    A a; 
    T b; 
    B(){std::cout<<"B()"<<std::endl;} 
}; 

int main() 
{ 
    B<B<B<int> > > Test; 
    return 0; 
} 

L'odrer dei costruttori di chiamata èOrdine dei costruttori di chiamata in un caso C++

A() 
A() 
A() 
B() 
B() 
B() 

e non ho indizio perché è questo. Pensavo che sarebbe stato A B A B A B. Potresti spiegarmi perché?

+3

variabili membro sono inizializzate prima che il corpo del costruttore viene eseguito. – immibis

risposta

2

Questo è in realtà semplice, se era come ABABAB, allora si dovrebbe avere problemi se si vuole accedere b dal costruttore di B, dal momento che l'ordine si pensava implica che il primo membro a viene creata un'istanza, quindi ctor piste, quindi b viene inizializzato. In realtà, ogni membro viene istanziato (costruito ecc.) Prima, quindi, vengono chiamati i costruttori.

1

Questo perché le variabili membro devono essere inizializzate prima dell'esecuzione del corpo del costruttore. Si consideri il seguente esempio:

struct A { 
    int value; 

    // Here we explicitly initialize 'value' with 5 
    A() : value(5) { } 
}; 

struct B { 
    A a; 

    B() 
    { 
     // This is perfectly valid and would print 5, 
     // because 'a' has already been implicitly initialized 
     // with its default constructor. 
     std::cout << a.value; 
    } 
}; 

Se così non fosse, quale valore ci si può aspettare di avere a nel costruttore B s'? Ti imbatteresti in tutti i tipi di problemi. Pertanto, il costruttore predefinito di A deve essere chiamato in modo implicito prima del corpo di B().

In sostanza, per renderlo più esplicito, questo è ciò che sta accadendo:

// Initialize 'a' before body of constructor 
    B() : a() 
    { 
     std::cout << a.value; 
    } 
1

Prima di tutto, cerchiamo di analizzare ciò che avete qui:

  • si dispone di un oggetto Test di class B<B<B<int> > > , che è:

    class B<B<B<int> > > { 
        A a; 
        B<B<int> > b; 
    }; 
    
  • il seco campo ND di Test, Test.b è di class B<B<int> >, che è:

    class B<B<int> > { 
        A a; 
        B<int> b; 
    }; 
    
  • allora si ha il secondo campo di Test.b, Test.b.b, che è di class B<int>, che è:

    class B<int> { 
        A a; 
        int b; 
    }; 
    

quindi l'ordine di inizializzazione è:

  • A() per Test.a.
  • A() per Test.b.a.
  • A() per Test.b.b.a.
  • nessun costruttore come Test.b.b.b è di tipo int e non dispone di costruttore.
  • B<int>() per Test.b.b.
  • B<B<int> >() per Test.b.
  • B<B<B<int> > >() per Test.

Sfortunatamente, tutti e tre i costruttori scrivono su output la stessa cosa: B(), ma sono costruttori diversi per classi diverse.

+0

È possibile visualizzare il nesting: http://ideone.com/45rDMR – stefan

+0

Sì, ma si sta stampando l'ordine opposto, prima viene stampato il campo 'b' più nidificato. L'ordine potrebbe essere 'B() 2',' B() 1', 'B() 0'. –

+0

Non capisco quello che stai dicendo. L'ordine è B (), B >(), ... quindi i numeri visualizzati sono corretti: la prima chiamata a un costruttore di un oggetto B () da compilare è B, quindi B() 0 è correttamente stampato prima L'ordine non può differire affatto, è ben definito. – stefan

0

Questo è il comportamento previsto, poiché l'inizializzazione del membro avviene prima del corpo del costruttore. Per realizzare questo, è utile aggiungere i inizializzatori utente vedere:

template<typename T> 
struct B 
{ 
    A a; 
    T b; 
    B() 
    : 
     a(), 
     b() 
    { 
     std::cout<<"B()"<<std::endl; 
    } 
}; 

per cogliere appieno l'ordine di esecuzione, aggiungiamo un campo intero fittizio. Ho anche aggiunto un modello per visualizzare il nesting. Vedi http://ideone.com/KylQQb per una demo.

#include <cstdio> 

struct A 
{ 
    A() 
    : 
    dummy(printf("Starting to construct A()\n")) 
    { 
     printf("Fully constructed A()\n"); 
    } 
    int dummy; 
}; 

template <typename T> 
struct Nesting; 

template <> 
struct Nesting<int> 
{ 
    constexpr static int value = 0; 
}; 

template <template <typename> class T, typename I> 
struct Nesting<T<I>> 
{ 
     constexpr static int value = 1 + Nesting<I>::value; 
}; 

template<typename T> 
struct B 
{ 
    int dummy; 
    A a; 
    T b; 
    B() 
    : 
    dummy(printf("Starting to construct B() with nesting %d\n", Nesting<B<T>>::value)), 
    a(), 
    b() 
    { 
     printf("Fully constructed B() with nesting %d\n", Nesting<B<T>>::value); 
    } 
}; 

int main() 
{ 
    B<B<B<int>>> Test; 
    return 0; 
} 

Il risultato di questo sarà

Starting to construct B() with nesting 3 
Starting to construct A() 
Fully constructed A() 
Starting to construct B() with nesting 2 
Starting to construct A() 
Fully constructed A() 
Starting to construct B() with nesting 1 
Starting to construct A() 
Fully constructed A() 
Fully constructed B() with nesting 1 
Fully constructed B() with nesting 2 
Fully constructed B() with nesting 3 
+0

Penso che tu sia un po 'confuso. I costruttori non iniziano a essere chiamati e nel mezzo vengono chiamati altri costruttori. I costruttori sono un threaded e serializzati per un oggetto completamente da costruire. –

+0

@LuisColorado Non sono affatto confuso. Forse la denominazione non è eccezionale, ma questo è ciò che sta accadendo e ciò che è definito dallo standard. – stefan

Problemi correlati