2010-02-26 14 views
11

Si una clase tiene solo un constructor con un parámetro, ¿cómo declarar una matriz? Sé que el vector se recomienda en este caso. Por ejemplo, si tengo una clase¿Cómo declaro una matriz de objetos cuya clase no tiene un constructor predeterminado?

class Foo{ 

public: 
Foo(int i) {} 

} 

Cómo declarar una matriz o un vector que contiene 10000 objetos Foo?

+1

Por favor, no concluir a partir de las respuestas que usted no puede hacer referencia a la matriz si no inicializa de inmediato. Siempre puede hacer 'extern Foo foo [100];' y luego ya hacer referencia a la matriz, siempre y cuando la defina y * luego * necesite todos los inicializadores :) –

+0

Dagnammit, escribí el mismo comentario sobre 'extern ', pero pensé que debería comprobar que realmente funciona antes de publicar, y tú me ganaste.No creo que necesite siquiera definirlo, siempre y cuando no lo haga referencia. –

+1

¿Por qué declarar una matriz y no un vector? –

Respuesta

16

Para una matriz, debe proporcionar un inicializador para cada elemento de la matriz en el punto donde defina la matriz.

Para un vector puede proporcionar una instancia para copiar para cada miembro del vector.

p. Ej.

std::vector<Foo> thousand_foos(1000, Foo(42)); 
+0

Downvoter, ¿desea sugerir mejoría/corrección? –

+0

Para una matriz simple, solo estoy diciendo que no * tienes * para hacerlo de esa manera es todo ... parece engañoso. Hay otras alternativas prácticas. Además, el enfoque vectorial que se muestra aquí no permite llamar al constructor con diferentes argumentos. –

+0

@Dan Molding: Pero no se puede _definir_ una matriz de un tipo de clase sin un constructor predeterminado sin proporcionar los inicializadores. Estoy de acuerdo con esta declaración. La pregunta requiere una matriz o un vector y no da ninguna orientación sobre los valores necesarios, por lo que aún no creo que mi respuesta sea incorrecta o inútil, incluso si hay otras posibilidades. –

4

Tendría que hacer una serie de indicaciones a Foo.

Foo* myArray[10000]; 
for (int i = 0; i < 10000; ++i) 
    myArray[i] = new Foo(i); 
+3

En la mayoría de los casos, esto no sería una solución (es decir, los objetos Foo deben estar contiguos). Esto cambia el escenario mucho más que usar un 'std :: vector' en el primer caso. –

+0

de acuerdo. Me perdí completamente la palabra 'vector' en su oración final. Pensé que su 'Sé que los vectores son recomendados' intentaba decir 'Sé que los vectores son la solución correcta, pero de todos modos quiero una matriz'. –

12

En realidad, puede hacerlo, siempre se utiliza una lista de inicialización, como

Foo foos[4] = { Foo(0),Foo(1),Foo(2),Foo(3) }; 

sin embargo, con 10000 objetos esto es absolutamente impracticable. Ni siquiera estoy seguro de si estabas lo suficientemente loco como para intentar si el compilador aceptaría una lista de inicialización tan grande.

+5

Puede escribir un poco menos (si el constructor no es explícito): 'Foo foos [] = {0, 1, 2, 3};' –

+0

Mientras escribe, eso no es práctico. Sin embargo, Skydoor preguntó sobre matriz o vector. – sbi

+0

@David Rodríguez - dribeas ¡Oh seguro! Yo sé sobre eso. Pero por el bien del ejemplo, preferí ser explícito. @sbi Entiendo tu punto. Tal vez debería haber dicho considerando cuán impráctico es usar un vector en su lugar. – fogo

2

Cuando declara un objeto que no tiene un constructor predeterminado, debe inicializarlo en la declaración.

Foo a; // not allowed 
Foo b(0); // OK 

Lo mismo ocurre con las matrices de tales tipos:

Foo c[2]; // not allowed 
Foo d[2] = { 0, 1 }; // OK 
Foo e[] = { Foo(0), Foo(1), Foo(2) }; // also OK 

En su caso, es probable que encuentre que no sea práctico para inicializar todos los 10.000 elementos por el estilo, por lo que es posible que desee volver a pensar si el clase realmente no debería tener un constructor predeterminado.

+0

Me pregunto por qué la gente siempre insiste en hacer un reparto explícito entre' {} '... – AnT

+2

No insisto, @Andrey. Simplemente no siempre recuerdo si ocurren conversiones implícitas en los inicializadores de array. Editado para mostrar ambas sintaxis. La sintaxis explícita sería necesaria para los constructores de parámetros múltiples, pero es opcional para constructores de un solo parámetro y no explícitos. –

+0

Pero eso no es realmente una opción con los 10000 objetos dados, ¿o sí? – sbi

1

usted tiene que utilizar el inicializador agregado, con 10.000 de inicializadores inidividual entre el {}

Foo array[10000] = { 1, 2, 3, ..., 10000 }; 

Por supuesto, especificando 10000 inicializadores es algo del reino de lo imposible, pero se preguntó por él mismo. Querías declarar una matriz de 10000 objetos sin un constructor predeterminado.

+0

Tenga en cuenta que la pregunta fue sobre 10000 objetos. Eso hace que un archivo bastante grande se trague para el compilador. – sbi

1

La única manera de definir una matriz de una clase sin constructor por defecto sería para inicializar inmediato - no es realmente una opción con 10000 objetos.

Puede, sin embargo, asignar suficiente memoria sin procesar de la manera que desee y usar la ubicación new para crear los objetos en esa memoria.Pero si usted quiere hacer esto, es mejor utilizar std::vector que hace exactamente eso:

#include <iostream> 
#include <vector> 

struct foo { 
    foo(int) {} 
}; 

int main() 
{ 
    std::vector<foo> v; 
    v.resize(10000,foo(42)); 
    std::cout << v.size() '\n'; 
    return 0; 
} 
+0

* ¿Por qué * es mejor usar 'std :: vector' si hacen lo mismo? ¿Debido al aumento del 50% en el tamaño del código que obtiene al usar STL? ;) –

+1

@Dan: es mejor porque evita errores comunes. (Y ese aumento de tamaño es simplemente FUD). – sbi

+0

¿FUD simple? En un sistema integrado en el que estoy trabajando, el uso de solo * una * instancia de un vector hizo que mi tamaño de biblioteca compartida saltara de ~ 100 Kb a bastante más de 1 Mb. Eso no es FUD. Y, en este caso, es un aumento que no puedo pagar. Esto es con GCC. –

0

Otra opción podría ser usar una gran variedad de boost::optional<Foo>:

boost::optional<Foo> foos[10]; // No construction takes place 
           // (similar to vector::reserve) 

foos[i] = Foo(3); // Actual construction 

Una advertencia es que se le tener que acceder a los elementos con la sintaxis de puntero:

bar(*foos[2]); // "bar" is a function taking a "Foo" 

std::cout << foos[3]->baz(); // "baz" is a member of "Foo" 

también debe tener cuidado de no acceder a un elemento no inicializado.

Otra advertencia es que este no es un verdadero reemplazo para una matriz de Foo, ya que no podrá pasarlo a una función que espera lo último.

+0

Buena idea, pero requiere 'Foo' tener un constructor de copia, ¿no? –

+0

@Mark - Buen punto. Pero puede eludir esta limitación con un pequeño truco: http://codepad.org/csde4mwT – Manuel

0

En línea recta C, usando int foo[10000] = {1}; se inicializará el primer elemento de matriz a 1 y el resto de la matriz a cero. ¿C++ no autoinicializa miembros de matriz no especificados, o esto requiere un constructor predeterminado?

+0

En C++, los restantes miembros de la matriz son * value-initialized *, que en este caso requeriría un constructor predeterminado. – AnT

6

sbi tenía la mejor respuesta para arreglos simples, pero no daba un ejemplo. Así que ...

Debe utilizar la colocación de nuevo:

char *place = new char [sizeof(Foo) * 10000]; 
Foo *fooArray = reinterpret_cast<Foo *>(place); 
for (unsigned int i = 0; i < 10000; ++i) { 
    new (fooArray + i) Foo(i); // Call non-default constructor 
} 

Tenga en cuenta que cuando se utiliza la colocación nueva, que es responsable de llamar destructores de los objetos - el compilador no hacerlo para ti:

// In some cleanup code somewhere ... 
for (unsigned int i = 0; i < 10000; ++i) { 
    fooArray[i].~Foo(); 
} 

// Don't forget to delete the "place" 
delete [] reinterpret_cast<char *>(fooArray); 

Esta es la única vez que ves una llamada explícita legítima a un destructor.

NOTA: La primera versión de esto tenía un error sutil al eliminar el "lugar". Es importante volver a colocar el "lugar" en el mismo tipo que se renovó. En otras palabras, fooArray debe volver a fundirse al char * al eliminarlo. Vea los comentarios a continuación para una explicación.

+1

No estoy bajoneando ya que no hay nada fundamentalmente erróneo con el enfoque, pero puede obtener el mismo efecto con 'std :: vector ', 'reserve' y' push_back' y es mucho más fácil hacerlo más robusto y la excepción es segura. –

+1

Sí. Cuando escribí esto, aparentemente había pasado por alto el "o vector" al final de la pregunta, y me estaba enfocando completamente en la pregunta resumida en el título. Siendo ese el caso, esta es posiblemente la * única * forma práctica de hacerlo. Ciertamente no diría que esto es mejor que usar un vector, pero si * necesita * una matriz simple ... –

+0

No hay necesidad de llamar a los destructores manualmente. De hecho, esto es dañino, porque 'delete [] fooArray' ya lo hace antes de reactivar la memoria. Además, reemplazaría las primeras dos líneas con 'Foo * fooArray = reinterpret_cast (operador nuevo (10000 * sizeof (Foo)))'. – fredoverflow

0

Si tiene sentido para su clase, se podría proporcionar un valor predeterminado para el parámetro constructor:

class Foo 
{ 
public: 
    explicit Foo(int i = 0); 
} 

Ahora usted tienen un constructor predeterminado. (Un "constructor predeterminado" es un constructor al que se puede llamar sin argumentos: FAQ)

También recomendaría hacer explícito su constructor, como hice anteriormente. Le impedirá obtener Foo s de int s cuando no desee solicitarlos.

1
class single 
{ 
    int data; 
public: 
    single() 
    { 
     data = 0; 
    } 
    single(int i) 
    { 
     data = i; 
    } 
}; 

// in main() 
single* obj[10000]; 
for (unsigned int z = 0; z < 10000; z++) 
{ 
    obj[z] = new single(10); 
} 
0

Pruebe esto.

Foo **ppInstances=0; 
    size_t total_instances = 10000; 

    for(int parent=0;parent < total_instances;parent++){ 
     ppInstances[parent]=new Foo(parent); 
     ppInstances++; 
    } 
    for(int parent=0;parent < total_instances;parent++){ 
     delete *ppInstances; 
     ppInstances--; 
    } 

Cuestiones relacionadas