2010-03-22 15 views
30

Quiero crear en C++ una matriz de Objetos sin usar STL.C++, matriz de objetos sin <vector>

¿Cómo puedo hacer esto?

¿Cómo podría crear una matriz de Object2, que no tiene un constructor sin argumento (constructor predeterminado)?

+10

Fuera de interés, ¿por qué no quieres usar stl? – Sean

+0

@osgx: ¿Por qué no simplemente definir un constructor predeterminado? – kennytm

+4

@KennyTM Esa no es una buena solución. Muchas clases no pueden ni deben ser predeterminables. –

Respuesta

33

Si el tipo en cuestión tiene un constructor sin argumentos, utilice new[]:

Object2* newArray = new Object2[numberOfObjects]; 

no se olvide de llamar delete[] cuando ya no se necesita la matriz:

delete[] newArray; 

Si no es así No tiene tal constructor use operator new para asignar memoria, luego invoca constructores:

//do for each object 
::new(addressOfObject) Object2(parameters); 

De nuevo, no olvide desasignar la matriz cuando ya no la necesite.

+3

Object2 no tiene un editor predeterminado. – kennytm

+0

me rompiste el cerebro con esta construcción: :: new (address) Object2 (parameters); – Andrey

+7

@Andrey: Es una vieja colocación simple nueva. – sharptooth

0

Si no hay un constructor predeterminado disponible, necesitará una matriz de punteros y luego recorra esa matriz para inicializar cada uno de los punteros.

+2

Es poco probable que ser rápido: problemas de asignación y errores de caché son posibles. –

5
Object2 *myArray[42]; 
for (int i = 0; i < 42; i++) 
{ 
    myArray[i] = new Object2(param1, param2, ...); 
} 

Más adelante se tendrá que caminar a través de la matriz y desasignar cada miembro individual:

for (int j = 0; j < 42; j++) 
{ 
    delete myArray[j]; 
} 
+0

'Object2 myArray [42];' este paso no funcionará a menos que 'Object2' tenga un constructor predeterminado (declarado por el usuario o de otro modo). –

+1

@Charles Bailey. Sí, yo noté que un segundo después de que lo publiqué, y lo cambié a una serie de punteros. –

+0

heeey. No es una matriz de objetos, ¡es una matriz de indicadores! – osgx

0

La pregunta obvia es por qué usted no desea utilizar la STL.

Suponiendo que tenga un motivo, crearía una matriz de objetos con algo como Obj * op = new Obj[4];. Solo recuerde deshacerse de él con delete [] op;.

No puede hacer eso con un objeto sin un constructor que no tome argumentos. En ese caso, creo que lo mejor que puedes hacer es asignar algo de memoria y usar la ubicación nueva. No es tan sencillo como los otros métodos.

+0

Estoy intentando implementar estructuras tipo stl desde cero. no será un vector, sino un árbol B de vectores de longitud constante. Y quiero un control total, así que quiero implementarlo completamente solo – osgx

3

utilizar una matriz de punteros a Object2:

std::tr1::shared_ptr<Object2>* newArray = new shared_ptr<Object2>[numberOfObjects]; 
for(int i = 0; i < numberOfObjects; i++) 
{ 
    newArray[i] = shared_ptr<Object2>(new Object2(params)); 
} 

O, alternativamente, sin el uso de shared_ptr:

Object2** newArray = new Object2*[numberOfObjects]; 
for(int i = 0; i < numberOfObjects; i++) 
{ 
    newArray[i] = new Object2(params); 
} 
+1

Como otros señalan, esto agrega una gran cantidad de sobrecarga de uso de memoria (nosotros es un montón de bloques de montón, cada uno de los cuales cuesta un encabezado de montón) y rompe la localidad espacial. –

+0

@Ben Voigt, ¿qué quiere decir "montón de bloques de montón"? ¿Cuánto tiempo es el encabezado del montón y es necesario para cada 'nuevo Object2 (params)'? – osgx

+0

El tamaño del encabezado del montón varía según la configuración del compilador y si el montón de depuración está en uso o no, pero para las implementaciones con las que estoy familiarizado es un _minimum_ de 2 * sizeof (void *), que es de 8 bytes para un 32- proceso de bit Cada llamada a :: new, :: new [], o malloc tiene que incluir espacio para el encabezado del montón dentro del bloque de pila que devuelve, y la dirección de devolución está garantizada que es un múltiplo de sizeof (void *). No sería inusual que el nuevo char() asigne 64 bytes o más en una compilación de depuración, que tiene comprobaciones de subdesbordamiento, desbordamiento, registros de línea haciendo asignación, etc. –

4

Usted puede hacer lo std::vector hace y crear un bloque de memoria en bruto. A continuación, construye los objetos que no tienen un constructor predeterminado en esa memoria utilizando la colocación nueva, según se requiera. Pero, por supuesto, si haces eso también podrías haber usado std::vector en primer lugar.

10

Suponiendo que su clase es la base y que tiene un constructor un argumento

Base arr[3] = {Base(0), Base(1), Base(2)} ; 
+1

Esta es la mejor solución si conoce el tamaño de la matriz en tiempo de compilación, si no va con el vector realmente. – smerlin

+0

Sé el tamaño en el tiempo de construcción del contenedor solamente. – osgx

13
// allocate memory 
Object2* objArray = static_cast<Object2*>(::operator new (sizeof Object2 * NUM_OF_OBJS)); 
// invoke constuctors 
for (size_t i = 0; i < NUM_OF_OBJS; i++) 
    new (&objArray[i]) Object2(/* initializers */); 

// ... do some work 

// invoke destructors 
for (size_t i = 0; i < NUM_OF_OBJS; i++) 
    objArray[i].~Object2(); 

// deallocate memory 
::operator delete (objArray); 
+0

¿Funciona static_cast así? (Siempre imaginé que necesitabas reinterpret_cast para pasar del vacío * a cualquier otra cosa, pero honestamente nunca lo he intentado con static_cast.) ... y olvidaste destruir tus objetos antes de eliminar la matriz, necesitas llamar explícitamente al dtor: para (...) objArray [i]. ~ Object2(); –

+0

+1/más para ir/ – osgx

+0

¿Por qué necesitamos aquí un static_cast no el simple '(Object2 *)' uno? – osgx

0

Si realmente necesita un array (secuencia contigua de objetos) de un tipo construible no predeterminada y por alguna razón puede molestar al usuario std::vector (!?) luego debe usar una función de asignación sin procesar y la ubicación nueva.

Esto es muy difícil de hacer de manera confiable; esto debería ayudar a mostrar por qué. Este fragmento incluye alguna defensa contra excepciones, pero es más que probable que no sea robusto contra todas las fallas.

const size_t required_count = 100; //e.g. 

// cast to pointer of required type needed for pointer arithmetic 
Object2* objarray = static_cast<Object2*>(operator new(required_count * sizeof(Object2))); 

size_t construction_count = 0; 

try 
{ 
    while (construction_count < required_count) 
    { 
     // params could change with index. 
     new (static_cast<void*>(objarray + construction_count)) Object2(param1, param2); 
     ++construction_count; 
    } 
} 
catch (...) 
{ 
    while (construction_count-- != 0) 
    { 
     try 
     { 
      (&objarray[construction_count])->~Object2(); 
     } 
     catch (...) 
     { 
      // not a lot we can do here, log but don't re-throw. 
     } 
    } 

    operator delete(objarray); 
    throw; 
} 

// Now objarray has been allocated and pointer to an array of required_count Object2 
// It cannot be de-allocated via delete[] or delete; you must loop through 
// calling destructors and then call operator delete on the buffer. 
+1

Hay muchas buenas razones para evitar STL, y los que evitan STL también tienden a evitar excepciones. ;) –

+0

¿Puedo hacer una construcción de objeto sin ubicación nueva, pero usando 'objarray [i] -> Object2 (param1, param2)'? – osgx

+0

@ dash-tom-bang: Estoy tratando de ser lo más neutral posible a la excepción, solo protegiéndome contra las excepciones generadas por la falla de asignación de memoria y el objeto del cliente. No estoy lanzando ninguna nueva excepción que otras soluciones no lo son; están (en general) simplemente ignorando posibles excepciones que yo no soy. –

Cuestiones relacionadas