2008-09-23 8 views
14

En C++, no es posible inicializar los miembros de la matriz en la lista de inicialización, por lo que los objetos miembros deben tener constructores predeterminados y deben inicializarse correctamente en el constructor. ¿Hay alguna solución (razonable) para esto aparte de no usar matrices?¿Alguna solución para la inicialización de matriz de miembros no estáticos?

[Cualquier cosa que pueda ser inicializado usando sólo la lista de inicialización está en nuestra aplicación mucho más preferible que mediante el constructor, como que los datos pueden ser asignados e inicializado por el compilador y enlazador, y cada número de ciclos de reloj de la CPU, incluso antes de main . Sin embargo, no siempre es posible tener un constructor predeterminado para cada clase, y además, la reinicialización de los datos nuevamente en el constructor en lugar de eso frustra el propósito.]

P. ej. Me gustaría tener algo como esto (pero éste no funciona):

class OtherClass { 
private: 
    int data; 
public: 
    OtherClass(int i) : data(i) {}; // No default constructor! 
}; 

class Foo { 
private: 
    OtherClass inst[3]; // Array size fixed and known ahead of time. 
public: 
    Foo(...) 
     : inst[0](0), inst[1](1), inst[2](2) 
     {}; 
}; 

La única solución que soy consciente de es la no-array uno:

class Foo { 
private: 
    OtherClass inst0; 
    OtherClass inst1; 
    OtherClass inst2; 
    OtherClass *inst[3]; 
public: 
    Foo(...) 
     : inst0(0), inst1(1), inst2(2) { 
     inst[0]=&inst0; 
     inst[1]=&inst1; 
     inst[2]=&inst2; 
    }; 
}; 

Editar: Cabe destacar que OtherClass no tiene un constructor predeterminado, y que es muy conveniente que el enlazador sea capaz de asignar la memoria necesaria (se crearán una o más instancias estáticas de Foo), utilizando el montón es esencialmente verboten. He actualizado los ejemplos anteriores para resaltar el primer punto.

Respuesta

4

Una posible solución consiste en evitar que el compilador llame al constructor OtherClass y llamarlo por su cuenta con la colocación nueva para inicializarlo de la manera que lo necesite. Ejemplo:

class Foo 
    { 
    private: 
    char inst[3*sizeof(OtherClass)]; // Array size fixed. OtherClass has no default ctor. 

    // use Inst to access, not inst 
    OtherClass &Inst(int i) {return (OtherClass *)inst+i;} 
    const OtherClass &Inst(int i) const {return (const OtherClass *)inst+i;} 
    public: 
    Foo(...) 
    { 
     new (Inst(0)) OtherClass(...); 
     new (Inst(1)) OtherClass(...); 
     new (Inst(2)) OtherClass(...); 
    } 
    ~Foo() 
    { 
     Inst(0)->~OtherClass(); 
     Inst(1)->~OtherClass(); 
     Inst(2)->~OtherClass(); 
    } 
    }; 

para atender a posibles requisitos de alineación de la OtherClass, puede que tenga que utilizar __declspec (alinear (x)) si se trabaja en VisualC++, o utilizar un tipo distinto de carbón como:

Type inst[3*(sizeof(OtherClass)+sizeof(Type)-1)/sizeof(Type)]; 

... donde Tipo es int, double, long long, o lo que sea que describa los requisitos de alineación.

+0

No olvide agregar "#include " para la ubicación nueva :) – Drealmer

+0

Tendré que comprobar el rendimiento de la ubicación nueva en mi plataforma, pero de lo contrario esto parece una solución de trabajo, aunque podría no ser comprensible mis colegas. :-) –

+0

He seleccionado esta respuesta, aunque por ahora probablemente conserve mi propia versión en uso real, ya que creo que es más legible y más rápida, aunque utiliza memoria adicional para la matriz de punteros de conveniencia. –

2

¿Qué datos pertenecen a OtherClass? ¿La inicialización del valor será suficiente para esa clase?

Si el valor de inicialización es suficiente, entonces se puede valorar a inicializar una matriz en la lista de miembros de inicialización:

class A { 
public: 
    A() 
    : m_a() // All elements are value-initialized (which for int means zero'd) 
    { 
    } 

private: 
    int m_a[3]; 
}; 

Si los tipos de elementos de matriz son los tipos de clases, entonces el constructor por defecto se llamará.

EDITAR: Solo para aclarar el comentario de Drealmer.

Donde el tipo de elemento no es POD, entonces debe tener un "constructor por defecto accesible" (como se indicó anteriormente). Si el compilador no puede llamar al constructor predeterminado, esta solución no funcionará.

En el siguiente ejemplo, no funcionaría con este enfoque:

class Elem { 
public: 
    Elem (int); // User declared ctor stops generation of implicit default ctor 
}; 

class A { 
public: 
    A() 
    : m_a()   // Compile error: No default constructor 
    {} 

private: 
    Elem m_a[10]; 
}; 
+0

No funcionará con una matriz de un tipo sin un constructor predeterminado, pero buen truco de todos modos. – Drealmer

+0

La inicialización del valor de las matrices miembros no es compatible con el estándar. Sin embargo, funciona con Visual Studio. – andriej

+0

@ user467799: C++ '03 8.5/5: 'Para inicializar el valor de un objeto de tipo T significa:' ... '- si T es un tipo de matriz, entonces cada elemento se inicializa en valor'. –

0

miembros de la matriz no se inicializan por defecto.Por lo tanto, podría usar una función auxiliar estática que realice la inicialización y almacenar el resultado de la función auxiliar en un miembro.

#include "stdafx.h" 
#include <algorithm> 
#include <cassert> 

class C { 
public: // for the sake of demonstration... 
    typedef int t_is[4] ; 
    t_is is; 
    bool initialized; 

    C() : initialized(false) 
    { 
    } 

    C(int deflt) 
    : initialized(sf_bInit(is, deflt)) 
    {} 

    static bool sf_bInit(t_is& av_is, const int i){ 
    std::fill(av_is, av_is + sizeof(av_is)/sizeof(av_is[0]), i); 
    return true; 
    } 
}; 

int _tmain(int argc, _TCHAR* argv[]) 
{ 

    C c(1), d; 

    assert(c.is[0] == 1); 

    return 0; 
} 

Vale la pena señalar que en el siguiente estándar, van a ser compatibles con los inicializadores de matriz.

+0

Aún queda la raíz del problema, necesita un constructor predeterminado para el tipo de elemento de matriz. – Drealmer

0

utilizar la herencia para crear objeto proxy

class ProxyOtherClass : public OtherClass { 
public: 
    ProxyOtherClass() : OtherClass(0) {} 
}; 

class Foo { 
private: 
    ProxyOtherClass inst[3]; // Array size fixed and known ahead of time. 
public: 
    Foo(...) {} 
}; 
+0

Pude haber leído la respuesta demasiado rápido, pero no creo que funcione, ya que cada instancia de OtherClass necesita diferentes parámetros para el constructor. –

0

Y qué acerca del uso de matriz de punteros en lugar de matriz de objetos? Por ejemplo:

class Foo { 
private: 
    OtherClass *inst[3]; 
public: 
    Foo(...) { 
     inst[0]=new OtherClass(1); 
     inst[1]=new OtherClass(2); 
     inst[2]=new OtherClass(3); 
    }; 

    ~Foo() { 
     delete [] inst; 
    } 

}; 
+0

El operador nuevo (cuando se usa como se indica anteriormente) asignará los objetos nuevos en el montón, lo cual no está permitido. –

0

se dice "Todo lo que puede ser inicializado usando sólo la lista de inicialización está en nuestra aplicación mucho más preferible que mediante el constructor, como que los datos pueden ser asignados e inicializado por el compilador y enlazador, y cada ciclo de reloj de la CPU cuenta ".

Por lo tanto, no use constructores. Es decir, no use "instancias" convencionales. Declara todo estáticamente Cuando necesite una nueva "instancia", cree una nueva declaración estática, potencialmente fuera de cualquier clase. Use estructuras con miembros públicos si es necesario. Use C si es necesario.

Respondió su propia pregunta. Los constructores y destructores solo son útiles en entornos con mucha asignación y desasignación. ¿De qué sirve la destrucción si el objetivo es asignar tantos datos como sea posible de forma estática, y entonces, ¿de qué sirve la construcción sin destrucción? Al diablo con los dos.

1

Un método Me suelen utilizar para hacer un miembro de la clase "parece" estar en la pila (aunque en realidad se almacena en el montón):

class Foo { 
private: 
    int const (&array)[3]; 
    int const (&InitArray() const)[3] { 
     int (*const rval)[3] = new int[1][3]; 
     (*rval)[0] = 2; 
     (*rval)[1] = 3; 
     (*rval)[2] = 5; 
     return *rval; 
    } 
public: 
    explicit Foo() : array(InitArray()) { } 
    virtual ~Foo() { delete[] &array[0]; } 
};
Para clientes de su clase, variedad parece ser del tipo "const int [3 ] ". Combine este código con la ubicación nueva y también puede inicializar realmente los valores a su criterio utilizando cualquier constructor que desee. Espero que esto ayude.

Cuestiones relacionadas