2010-03-09 18 views
59

Sto avendo un crampo al cervello ... come faccio a inizializzare un array di oggetti correttamente in C++?C++: inizializzazione del costruttore per gli array

non-array Esempio:

struct Foo { Foo(int x) { /* ... */ } }; 

struct Bar { 
    Foo foo; 

    Bar() : foo(4) {} 
}; 

serie esempio:

struct Foo { Foo(int x) { /* ... */ } }; 

struct Baz { 
    Foo foo[3]; 

    // ??? I know the following syntax is wrong, but what's correct? 
    Baz() : foo[0](4), foo[1](5), foo[2](6) {} 
}; 

edit: selvatici & idee di soluzione folli sono apprezzati, ma non mi aiuteranno nel mio caso. Sto lavorando su un processore embedded dove std :: vector e altri costrutti STL non sono disponibili, e la soluzione ovvia è quella di fare un costruttore di default e hanno un init() metodo esplicito che può essere chiamato dopo la costruzione in tempo, in modo che io don Devo assolutamente usare gli inizializzatori. (Questo è uno di quei casi in cui sono stato rovinato dalla parola chiave Java + final + flessibilità con i costruttori.)

+0

Le classi non sono costruibili perché tutto è 'private'. –

+5

(parole chiave di accesso rimandate per semplicità pedagogica) –

+7

Non sarebbe più semplice usare 'struct' al posto di' class' per semplicità pedagogica? Trovo il codice che compila più facilmente da cui imparare ;-) –

risposta

48

Non c'è modo. È necessario un costruttore predefinito per i membri dell'array e verrà chiamato, in seguito, è possibile eseguire qualsiasi inizializzazione desiderata nel costruttore.

+8

Sfortunatamente, hai ragione. +1 Nota che la sintassi di inizializzazione unificata di C++ 1x ti consentirà di farlo. – sbi

+10

@sbi: lo chiamo ancora "C++ 0x". Certo, a volte ho anche la mia età di 38 anni e spero di andare in pensione all'età di 3 anni. –

+2

@David: Io lo chiamo C++ 1x (sperando che venga rilasciato entro i prossimi 9,5 anni). – sbi

1

Solo il costruttore di default può essere chiamato durante la creazione di oggetti in una matrice.

16

In questo momento, non è possibile utilizzare la lista di inizializzazione per membri di matrice. Sei bloccato a farlo nel modo più duro.

class Baz { 
    Foo foo[3]; 

    Baz() { 
     foo[0] = Foo(4); 
     foo[1] = Foo(5); 
     foo[2] = Foo(6); 
    } 
}; 

In C++ 0x si può scrivere:

class Baz { 
    Foo foo[3]; 

    Baz() : foo({4, 5, 6}) {} 
}; 
+0

verrà chiamato un costruttore di un argomento per un int a meno che non si dichiari esplicito il costruttore. – jmanning2k

+0

interessante ... Probabilmente avrei dovuto usare qualcosa oltre a 'int' quindi nel mio esempio, dato che è troppo" facile "da gestire. :-) –

1

Nel caso specifico in cui la matrice è un membro di dati della classe che si non possibile inizializzare nella versione corrente di la lingua. Non c'è sintassi per questo. Presentare una costruttore predefinito per elementi array o utilizzare std::vector.

Un array autonomo può essere inizializzato con inizializzatore aggregato

Foo foo[3] = { 4, 5, 6 }; 

ma purtroppo non c'è sintassi corrispondente per l'elenco costruttore inizializzatore.

0

Non c'è sintassi array costruzione che ca essere utilizzato in questo contesto, almeno non direttamente. Si può realizzare ciò che si sta cercando di realizzare da qualcosa sulla falsariga di:

Bar::Bar() 
{ 
    static const int inits [] = {4,5,6}; 
    static const size_t numInits = sizeof(inits)/sizeof(inits[0]); 
    std::copy(&inits[0],&inits[numInits],foo); // be careful that there are enough slots in foo 
} 

... ma avrete bisogno di dare Foo un costruttore di default.

2

Questo sembra funzionare, ma non sono convinto che sia giusto:

#include <iostream> 

struct Foo { int x; Foo(int x): x(x) { } }; 

struct Baz { 
    Foo foo[3]; 

    static int bar[3]; 
    // Hmm... 
    Baz() : foo(bar) {} 
}; 

int Baz::bar[3] = {4, 5, 6}; 

int main() { 
    Baz z; 
    std::cout << z.foo[1].x << "\n"; 
} 

uscita:

$ make arrayinit -B CXXFLAGS=-pedantic && ./arrayinit 
g++ -pedantic arrayinit.cpp -o arrayinit 
5 

Caveat emptor.

Edit: no, Comeau respinge.

Un'altra modifica: Questa è una specie di imbroglio, semplicemente spinge la matrice di inizializzazione di membri-by-utente in un luogo diverso.Quindi richiede ancora Foo di avere un costruttore di default, ma se non si dispone di std::vector allora è possibile implementare per voi stessi il minimo assoluto è necessario:

#include <iostream> 

struct Foo { 
    int x; 
    Foo(int x): x(x) { }; 
    Foo(){} 
}; 

// very stripped-down replacement for vector 
struct Three { 
    Foo data[3]; 
    Three(int d0, int d1, int d2) { 
     data[0] = d0; 
     data[1] = d1; 
     data[2] = d2; 
    } 
    Foo &operator[](int idx) { return data[idx]; } 
    const Foo &operator[](int idx) const { return data[idx]; } 
}; 

struct Baz { 
    Three foo; 

    static Three bar; 
    // construct foo using the copy ctor of Three with bar as parameter. 
    Baz() : foo(bar) {} 
    // or get rid of "bar" entirely and do this 
    Baz(bool) : foo(4,5,6) {} 
}; 

Three Baz::bar(4,5,6); 

int main() { 
    Baz z; 
    std::cout << z.foo[1].x << "\n"; 
} 

z.foo non è in realtà un array, ma sembra circa quanto uno come un vettore. L'aggiunta delle funzioni begin() e end() a Tre è banale.

+0

+1 per provare a risolvere il problema –

+0

... e questo mi dà alcune idee che potrebbero funzionare per la mia situazione in modo più pulito di quello che ho. Grazie! –

0

Idee da una mente contorta:

class mytwistedclass{ 
static std::vector<int> initVector; 
mytwistedclass() 
{ 
    //initialise with initVector[0] and then delete it :-) 
} 

}; 

ora impostate in questo initVector a qualcosa u vuole prima di u un'istanza di un oggetto. Quindi i tuoi oggetti sono inizializzati con i tuoi parametri.

7

Sfortunatamente non c'è modo di inizializzare i membri dell'array fino a C++ 0x.

È possibile utilizzare std :: vector e push_back le istanze di Foo nel corpo del costruttore.

Si potrebbe dare a Foo un costruttore predefinito (potrebbe essere privato e rendere Baz un amico).

si potrebbe usare un oggetto array che è copiabile (boost o std :: tr1) e inizializzate da una matrice statica:

#include <boost/array.hpp> 

struct Baz { 

    boost::array<Foo, 3> foo; 
    static boost::array<Foo, 3> initFoo; 
    Baz() : foo(initFoo) 
    { 

    } 
}; 

boost::array<Foo, 3> Baz::initFoo = { 4, 5, 6 }; 
+0

+1. Mi sono chiesto perché nessuno l'abbia inventato, finché non ho visto la tua risposta. 'array' è banale da implementare, e non è né selvaggio né pazzo. Potresti scrivere una funzione come 'array create() {array a = {...}; ritorno a; } 'anche per evitare la variabile statica. –

+0

Anche a me sembra ovvio, anche se il supporto per i template è debole sul compilatore di destinazione (non 'std :: vector' sembra pescoso) un approccio di generazione funzionerebbe (preprocessore o script che genera classi necessarie). –

3

È possibile utilizzare C++ 0xauto parola chiave insieme specializzazione modello, ad esempio una funzione denominata boost::make_array() (simile a make_pair()). Per il caso di cui N è 1 o 2 argomenti poi possiamo scrivere variante A come

namespace boost 
{ 
/*! Construct Array from @p a. */ 
template <typename T> 
boost::array<T,1> make_array(const T & a) 
{ 
    return boost::array<T,2> ({{ a }}); 
} 
/*! Construct Array from @p a, @p b. */ 
template <typename T> 
boost::array<T,2> make_array(const T & a, const T & b) 
{ 
    return boost::array<T,2> ({{ a, b }}); 
} 
} 

e variante B come

namespace boost { 
/*! Construct Array from @p a. */ 
template <typename T> 
boost::array<T,1> make_array(const T & a) 
{ 
    boost::array<T,1> x; 
    x[0] = a; 
    return x; 
} 
/*! Construct Array from @p a, @p b. */ 
template <typename T> 
boost::array<T,2> make_array(const T & a, const T & b) 
{ 
    boost::array<T,2> x; 
    x[0] = a; 
    x[1] = b; 
    return x; 
} 
} 

GCC-4.6 con -std=gnu++0x e -O3 genera il identico stesso codice binario per

auto x = boost::make_array(1,2); 

utilizzando sia A e B come per

boost::array<int, 2> x = {{1,2}}; 

Per definiti dall'utente tipi (UDT), però, risultati variante B nelle un'altra copia costruttore, che di solito rallentare le cose, e dovrebbe quindi essere evitato.

noti che boost::make_array errori quando si chiama con letterali esplicita matrice char come nel seguente caso

auto x = boost::make_array("a","b"); 

Credo che questa è una buona cosa in quanto const char* letterali può essere ingannevole nel loro uso.

variadic modelli, disponibili in GCC dal 4,5, può essere ulteriormente utilizzato ridurre tutto il codice boilerplate template specializzazione per ogni N in una definizione singolo modello di boost::make_array() definiti come

/*! Construct Array from @p a, @p b. */ 
template <typename T, typename ... R> 
boost::array<T,1+sizeof...(R)> make_array(T a, const R & ... b) 
{ 
    return boost::array<T,1+sizeof...(R)>({{ a, b... }}); 
} 

Funziona praticamente come ci aspettiamo. Il primo argomento determina l'argomento modello boost::arrayT e tutti gli altri argomenti vengono convertiti in T. Per alcuni casi questo potrebbe non essere desiderabile, ma non sono sicuro di come sia possibile specificare l'uso di modelli variadici.

Forse boost::make_array() dovrebbe entrare nelle librerie Boost?

+0

grazie, ma C++ 0x non è disponibile su processori embedded di fascia bassa (i compilatori C++ sono abbastanza difficili da trovare) –

-3
class C 
{ 
    static const int myARRAY[10]; // only declaration !!! 

    public: 
    C(){} 
    } 

const int C::myARRAY[10]={0,1,2,3,4,5,6,7,8,9}; // here is definition 

int main(void) 
{ 
    C myObj; 
    } 
25

Proprio per modificare questa domanda per C++ 11, questo è ora sia possibile fare e molto naturale:

struct Foo { Foo(int x) { /* ... */ } }; 

struct Baz { 
    Foo foo[3]; 

    Baz() : foo{{4}, {5}, {6}} { } 
}; 

Tali graffe possono anche essere tralasciata per una sempre più concisa:

struct Baz { 
    Foo foo[3]; 

    Baz() : foo{4, 5, 6} { } 
}; 

che può essere facilmente estesa ad array multidimensionali anche:

struct Baz { 
    Foo foo[3][2]; 

    Baz() : foo{1, 2, 3, 4, 5, 6} { } 
}; 
+0

C'è un bel modo per inizializzare Foo foo [3] [2] ;? – dshin

+2

@dshin Stesso modo. Il più rinforzato: ': foo {{{1}, {2}}, {{3}, {4}}, {{5}, {6}}}', o meno rinforzato 'foo {{1, 2} , {3, 4}, {5, 6}} ', o meno rinforzato' foo {1, 2, 3, 4, 5, 6} '. – Barry

+3

Esiste una soluzione alternativa quando il costruttore di 'Foo' è dichiarato esplicito? – dshin

-1

in Visual Studio 2012 o in precedenza, si può fare in questo modo

struct Foo { Foo(int x) { /* ... */ } }; 

struct Baz { 
    Foo foo[3]; 

    Baz() : foo() { } 
}; 
0

Si può fare, ma non è abbastanza:

#include <iostream> 

class A { 
    int mvalue; 
public: 
    A(int value) : mvalue(value) {} 
    int value() { return mvalue; } 
}; 

class B { 
    // TODO: hack that respects alignment of A.. maybe C++14's alignof? 
    char _hack[sizeof(A[3])]; 
    A* marr; 
public: 
    B() : marr(reinterpret_cast<A*>(_hack)) { 
     new (&marr[0]) A(5); 
     new (&marr[1]) A(6); 
     new (&marr[2]) A(7); 
    } 

    A* arr() { return marr; } 
}; 

int main(int argc, char** argv) { 
    B b; 
    A* arr = b.arr(); 
    std::cout << arr[0].value() << " " << arr[1].value() << " " << arr[2].value() << "\n"; 
    return 0; 
} 

Se mettete questo nel codice, vi auguro avere una buona ragione.

0

Questa è la mia soluzione per il vostro riferimento:

struct Foo 
{ 
    Foo(){}//used to make compiler happy! 
    Foo(int x){/*...*/} 
}; 

struct Bar 
{ 
    Foo foo[3]; 

    Bar() 
    { 
     //initialize foo array here: 
     for(int i=0;i<3;++i) 
     { 
      foo[i]=Foo(4+i); 
     } 
    } 
}; 
Problemi correlati