J'ai des crampes au cerveau ... comment initialiser correctement un tableau d'objets en C++?
exemple non-array:
struct Foo { Foo(int x) { /* ... */ } };
struct Bar {
Foo foo;
Bar() : foo(4) {}
};
exemple de tableau:
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: Les idées de contournement sauvages et folles sont appréciées, mais elles ne m'aideront pas dans mon cas. Je travaille sur un processeur intégré dans lequel std :: vector et d'autres constructions STL ne sont pas disponibles, et la solution évidente consiste à créer un constructeur par défaut et à utiliser une méthode explicite init()
pouvant être appelée après construction- temps, de sorte que je n'ai pas du tout utiliser d'initialiseurs. (C’est l’un des cas où j’ai été gâté par le final
mot-clé de Java + la flexibilité avec les constructeurs.)
Il n'y a pas moyen. Vous avez besoin d'un constructeur par défaut pour les membres du tableau et il sera appelé. Vous pourrez ensuite effectuer l'initialisation de votre choix dans le constructeur.
Juste pour mettre à jour cette question pour C++ 11, il est maintenant à la fois possible de le faire et très naturel:
struct Foo { Foo(int x) { /* ... */ } };
struct Baz {
Foo foo[3];
Baz() : foo{{4}, {5}, {6}} { }
};
Ces accolades peuvent également être supprimées pour un résultat encore plus concis:
struct Baz {
Foo foo[3];
Baz() : foo{4, 5, 6} { }
};
Ce qui peut facilement être étendu aux tableaux multidimensionnels aussi:
struct Baz {
Foo foo[3][2];
Baz() : foo{1, 2, 3, 4, 5, 6} { }
};
À l'heure actuelle, vous ne pouvez pas utiliser la liste d'initialisation pour les membres du groupe. Vous êtes coincé le faire à la dure.
class Baz {
Foo foo[3];
Baz() {
foo[0] = Foo(4);
foo[1] = Foo(5);
foo[2] = Foo(6);
}
};
En C++ 0x, vous pouvez écrire:
class Baz {
Foo foo[3];
Baz() : foo({4, 5, 6}) {}
};
Malheureusement, il n’existe aucun moyen d’initialiser les membres du tableau avant C++ 0x.
Vous pouvez utiliser un std :: vector et Push_back les occurrences de Foo dans le corps du constructeur.
Vous pouvez donner à Foo un constructeur par défaut (peut être privé et faire de Baz un ami).
Vous pouvez utiliser un objet tableau qui est copiable (boost ou std :: tr1) et initialiser à partir d'un tableau statique:
#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 };
Vous pouvez utiliser le mot clé C++ 0xauto
avec spécialisation de modèle sur, par exemple, une fonction nommée boost::make_array()
(similaire à make_pair()
). Dans le cas où N
est 1 ou 2 arguments, on peut alors écrire variante A comme
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 }});
}
}
et variante B comme
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 avec -std=gnu++0x
Et -O3
Génère le exact code binaire même pour
auto x = boost::make_array(1,2);
en utilisant à la fois A et B comme pour
boost::array<int, 2> x = {{1,2}};
Pour types définis par l'utilisateur (UDT), cependant, la variante B donne n constructeur de copie supplémentaire, ce qui ralentit généralement les choses et doit donc être évité.
Notez que les erreurs boost::make_array
Lors de l'appel avec des littéraux de tableau de caractères explicites comme dans le cas suivant
auto x = boost::make_array("a","b");
Je pense que c'est une bonne chose car les littéraux de const char*
Peuvent être trompeurs dans leur utilisation.
Modèles variés, disponible dans GCC depuis la version 4.5, peut également être utilisé pour réduire tous les codes de spécialisation de modèles chaudière-plaque pour chaque N
dans un définition de modèle unique de boost::make_array()
définie comme
/*! 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... }});
}
Cela fonctionne à peu près comme prévu Le premier argument détermine l'argument de modèle boost::array
T
et tous les autres arguments sont convertis en T
. Dans certains cas, cela peut être indésirable, mais je ne sais pas comment il est possible de spécifier à l'aide de modèles variadiques.
Peut-être que boost::make_array()
devrait aller dans les bibliothèques Boost?
Cela semble fonctionner, mais je ne suis pas convaincu que ce soit juste:
#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";
}
Sortie:
$ make arrayinit -B CXXFLAGS=-pedantic && ./arrayinit
g++ -pedantic arrayinit.cpp -o arrayinit
5
Caveat emptor.
Edit: non, Comeau le rejette.
Une autre édition: C'est une sorte de tricherie, cela pousse simplement l'initialisation du tableau membre par membre à un endroit différent. Donc, Foo doit toujours avoir un constructeur par défaut, mais si vous n'avez pas std::vector
, Alors vous pouvez implémenter pour vous-même le strict minimum dont vous avez besoin:
#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
N'est pas réellement un tableau, mais il ressemble à peu près à celui d'un vecteur. L'ajout de begin()
et end()
à trois est trivial.
Seul le constructeur par défaut peut être appelé lors de la création d'objets dans un tableau.
Dans le cas spécifique où le tableau est un membre de la classe, vous ne pouvez pas l'initialiser dans la version actuelle du langage. Il n'y a pas de syntaxe pour cela. Fournissez un constructeur par défaut pour les éléments de tableau ou utilisez std::vector
.
Un tableau autonome peut être initialisé avec un initialiseur agrégé
Foo foo[3] = { 4, 5, 6 };
mais malheureusement, il n'y a pas de syntaxe correspondante pour la liste d'initialisation du constructeur.
Vous pouvez le faire, mais ce n'est pas joli:
#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;
}
Si vous mettez cela dans votre code, j'espère que vous avez une TRÈS bonne raison.
Aucune syntaxe de construction de tableau ne peut être utilisée dans ce contexte, du moins pas directement. Vous pouvez accomplir ce que vous essayez d'accomplir en procédant comme suit:
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
}
... mais vous devrez donner à Foo un constructeur par défaut.
Ceci est ma solution pour votre référence:
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);
}
}
};
Idées d'un esprit tordu:
class mytwistedclass{
static std::vector<int> initVector;
mytwistedclass()
{
//initialise with initVector[0] and then delete it :-)
}
};
définissez maintenant ce initVector
sur quelque chose que vous voulez avant d'instancier un objet. Ensuite, vos objets sont initialisés avec vos paramètres.