C++: inicializador de constructores para matrices


Estoy teniendo un calambre cerebral... ¿cómo inicializo una matriz de objetos correctamente en C++?

Ejemplo sin arreglo:

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

struct Bar { 
     Foo foo;

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

Ejemplo de matriz:

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: Las ideas de soluciones salvajes y locas son apreciadas, pero no me ayudarán en mi caso. Estoy trabajando en un procesador embebido donde std:: vector y otras construcciones STL no están disponibles, y la solución obvia es hacer un constructor predeterminado y tener un método explícito init() al que se puede llamar después tiempo de construcción, para que no tenga que usar inicializadores en absoluto. (Este es uno de esos casos en los que me he echado a perder por la palabra clave de Java final + flexibilidad con los constructores.)

Author: Jason S, 2010-03-09

14 answers

No hay manera. Necesita un constructor predeterminado para los miembros de la matriz y se llamará, después, puede hacer cualquier inicialización que desee en el constructor.

 49
Author: AProgrammer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-28 18:35:27

Solo para actualizar esta pregunta para C++11, esto ahora es posible de hacer y muy natural:

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

struct Baz { 
     Foo foo[3];

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

Esos frenos también pueden ser elided para un aún más conciso:

struct Baz { 
     Foo foo[3];

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

Que también se puede extender fácilmente a matrices multidimensionales:

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

    Baz() : foo{1, 2, 3, 4, 5, 6} { }
};
 30
Author: Barry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-27 02:17:25

En este momento, no puede usar la lista inicializador para los miembros de la matriz. Estás atascado haciéndolo de la manera difícil.

class Baz {
    Foo foo[3];

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

En C++0x puedes escribir:

class Baz {
    Foo foo[3];

    Baz() : foo({4, 5, 6}) {}
};
 16
Author: Michael Kristofik,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-09 15:39:58

Desafortunadamente no hay manera de inicializar los miembros del array hasta C++0x.

Podría usar un std::vector y push_back las instancias Foo en el cuerpo del constructor.

Podrías darle a Foo un constructor por defecto (podría ser privado y hacer de Baz un amigo).

Puede usar un objeto array que es copiable (boost o std:: tr1) e inicializar desde un array estático:

#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 };
 7
Author: UncleBens,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-09 16:00:00

Puede usar C++0x auto palabra clave junto con plantilla especialización en por ejemplo una función llamada boost::make_array() (similar a make_pair()). Para el caso de donde N es 1 o 2 argumentos podemos entonces escribir variante A como

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 }});
}
}

And variant B as

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 y -O3 genera el exactamente el mismo código binario para

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

Usando A y B como lo hace para

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

Para tipos definidos por el usuario (UDT), sin embargo, la variante B resulta en un constructor de copia adicional, que generalmente ralentiza las cosas, y por lo tanto debe evitarse.

Tenga en cuenta que boost::make_array errores al llamarlo con literales de matriz de caracteres explícitos como en el siguiente caso

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

Creo que esto es algo bueno ya que const char*los literales pueden ser engañosos en su uso.

Plantillas variádicas, disponible en CCG desde el punto 4.5, se puede seguir utilizando reducir todo el código de placa de caldera de especialización de plantilla para cada N en una definición de plantilla única de boost::make_array() definida como

/*! 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... }});
}

Esto funciona más o menos como esperamos. El primer argumento determina boost::array argumento de plantilla T y todos los demás argumentos se convierten en T. Para algunos casos esto puede ser indeseable, pero no estoy seguro de cómo si esto es posible especificar el uso de plantillas variádicas.

Tal vez boost::make_array() debería entrar en el ¿Aumentar Bibliotecas?

 3
Author: Nordlöw,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-06-15 12:57:30

Esto parece funcionar, pero no estoy convencido de que sea correcto:

#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";
}

Salida:

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

Caveat emptor.

Editar: no, Comeau lo rechaza.

Otra edición: Esto es una especie de trampa, simplemente empuja la inicialización de la matriz miembro por miembro a un lugar diferente. Por lo tanto, todavía requiere que Foo tenga un constructor predeterminado, pero si no tiene std::vector, entonces puede implementar por sí mismo el mínimo absoluto que necesita:

#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 en realidad no es una matriz, pero se parece tanto a uno como a un vector. Agregar begin() y end() funciones a Tres es trivial.

 2
Author: Steve Jessop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-09 16:05:51

Solo se puede llamar al constructor predeterminado cuando se crean objetos en una matriz.

 1
Author: Robert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-09 14:51:15

En el caso específico cuando el array es un miembro de datos de la clase, no puede inicializarlo en la versión actual del lenguaje. No hay sintaxis para eso. Proporcione un constructor predeterminado para los elementos de matriz o use std::vector.

Una matriz independiente se puede inicializar con aggregate initializer

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

Pero desafortunadamente no hay una sintaxis correspondiente para la lista inicializador del constructor.

 1
Author: AnT,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-09 14:51:57

No existe una sintaxis de construcción de matrices que pueda usarse en este contexto, al menos no directamente. Usted puede lograr lo que está tratando de lograr por algo a lo largo de las líneas de:

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
}

...pero tendrás que darle a Foo un constructor predeterminado.

 0
Author: John Dibling,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-09 14:56:03

Ideas de una mente retorcida:

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

};

Ahora establece esto initVector a algo que u quiere antes de que u instancie un objeto. A continuación, los objetos se inicializan con sus parámetros.

 0
Author: the100rabh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-03-09 15:05:03

Puedes hacerlo, pero no es bonito:

#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 pones esto en tu código, espero que tengas una muy buena razón.

 0
Author: Moshev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-29 17:23:44

Esta es mi solución para su referencia:

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);
        }
    }
};
 0
Author: user7967189,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-05 06:47:06

En visual studio 2012 o superior, puedes hacer así

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

struct Baz { 
     Foo foo[3];

     Baz() : foo() { }
};
 -1
Author: bowman han,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-13 07:56:05
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;
   }
 -2
Author: jarjan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-10 18:43:09