Dynamic allocation of arrays o vectores en Cpp

Glumyglu

Buenas, soy un completo n00b de la programación y estoy siguiendo un par de libros para aprender a programar, en concreto orientado a los métodos numéricos y al cálculo científico. Pido disculpas si hay hilo de Cpp pero el buscador no me lo ha devuelto.

Estoy siguiendo "Parallel Scientific Computing in C++ and MPI" de George Em Karniadakis y Robert M. Kirby II. Es del 2003, por lo que asumo que algunas de las cosas que hacen pueden estar desfasadas y, creo que por esto, viene mi pregunta. En este libro para pasar una variable que guarda el número de elementos con los que inicializar un array lo hace con dynamic allocation. Por ejemplo:

int npts = 10;
double ** x;
x = new double*[npts];
...
delete[] x;

aunque el ejemplo no es muy bueno puesto que aquí "npts" es constante, pero asumamos que para mi problema no lo es (suele ser porque se introduce con un cin). El tamaño del array no cambia a lo largo del programa, es decir, el único problema es que el tamaño está guardado en una variable que no puedo declarar como constante.

Buscando si había forma de tener que desentenderme del "delete[]", he buscado en google y me he encontrado con que los vectores de la librería estándar siguen un poco la misma idea. Sin embargo, por lo poco que he leído son interesantes cuando el tamaño del vector va cambiando a lo largo del programa, que no es mi caso. De hecho, según otro libro que estoy siguiendo, "C++ Primer 5th Edition" de Lippman:

The standard requires that vector implementations can efficiently add elements at run time. Because vectors grow efficiently, it is often unnecessary—and can result in poorer performance—to define a vector of a specific size. The exception to this rule is if all the elements actually need the same value. If differing element values are needed, it is usually more efficient to define an empty vector and add elements as the values we need become known at run time. Moreover, as we’ll see in § 9.4 (p. 355), vector offers capabilities to allow us to further enhance run-time performance when we add elements. Starting with an empty vector and adding elements at run time is
distinctly different from how we use built-in arrays in C and in most other languages. In particular, if you are accustomed to using C or Java, you might expect that it would be best to define the vector at its expected size. In fact, the contrary is usually the case.

aunque esto contradice lo que he leído en StackExchange, sin embargo, supongo que prefiero fiarme del libro. Entonces tendría las opciones de inicializar vectores vacío e ir añadiendo elementos o inicializarlo con el tamaño que necesito y cambiarlos.

Expuesto esto, ¿cuál es la mejor forma de tratar mi problema? ¿Con la dynamic allocation de arrays, vectores inicializados como vacíos o vectores inicializados con el número de elementos (que sé de antemano) que necesito? ¿Alguna otra forma?

Cualquier cosa que no quede clara me puedo extender, es la primera vez que pregunto algo de programación y creo que me he explicado fatal. Pido disculpas de antemano por ello. Gracias.

Kike_Knoxvil

Si sabes de antemano cuantos elementos va a tener el array, mejor colocarselos al declararlo, ¿no?

int n
cin>>n
int/double/string arr[n]

Y ya luego vas metiendo los valores en el array

Al menos esto es lo que yo he entendido que quieres hacer

1 respuesta
The-Guest

Pues tengo poco conocimiento de programación en general y no sé exactamente la duda que tienes, pero creo que quizás estás confundiendo vectores (que pueden ser un string, una lista o un array, básicamente un puntero que apunta a un conjunto de datos) con el array en sí, el cual es un tipo de vector, de ahí que en el segundo extracto de C++ se refiera a los vectores como algo variable (listas o strings) pero no es el caso del array. De hecho lo hagas como lo hagas tienes que declarar la capacidad del array cuando lo vayas a querer utilizar, si te queda más claro declararlo en una línea e inicializarlo (indicarle la longitud del array) en otra lo puedes hacer perfectamente puesto que no hay una diferencia tremenda al hacerlo de una forma u otra.

Lo más eficiente y fino sería declarar e inicializar el array cuando lo vayas a usar en lugar de declararlo al principio del programa y tenerlo por ahí danzando.

No sé si te ayudo a resolver la duda, ni tan siquiera sé si todo lo que he dicho es correcto, así que no lo tomes al pie de la letra.

1 respuesta
Glumyglu

#2 hasta donde entiendo "arr[n]" daría error.

#3 es cuál de las tres opciones sería mejor (si hay una opción mejor). Sé que el array tienes que inicializarlo con su tamaño pero hasta donde sé:

int n=10;

array[n];

daría error.

Mientras que

array[10];

funciona. El problema es que yo quiero algo como lo de arriba (inicializar con una variable) y para eso, el libro propone usar el dynamic allocation ese (que no sé muy bien cómo funciona ese, la verdad).

Gracias a ambos por responder.

1 respuesta
refresco

me sorprende que C++ no tenga algo como fortran que te deje hacer un allocate.
No seria mucho menos eficiente crear un array vacio e ir llenandolo? Cada vez que añadas algo se debera buscar un hueco en memoria y mover todo el array no?

shaba

Hey,

Si tienes un upper bound (i.e sabes que tu funcion necesita un maximo de X size) es mejor hacer "allocation" en el stack ya que el programa ira bastante mas rapido...
en c++ esto se utiliza con std::array<int, X>. std::array te permite utilizar todas las funciones de la libreria (sort, fill etc)

Si X es dinamico o muy grande no tendras sitio en el stack y necesitaras la heap (dynamic allocation, mucho mas lenta).
En c++ std::vector<int>(X). Para el vector es importante inicializar siempre con el size si lo sabes.

https://godbolt.org/z/q4EsM4zf3

La otra forma es..
vector<int> a;
a.reserve(X);
a.emplace_back(1); // si el vector tiene ya X elementos, esta operacion puede crear una allocation para hacer el tamano del vector en memoria mas grande, con lo que es una operacion muy cara. Por eso es mejor siempre reservar el tamano si lo sabes

1
EUS

#4 Estas seguro que lo primero fallaría? La mejor manera de asegurarte es que lo programes y veas el fallo que te da. Me parece a mi que lo que fallaría es mas bien esto:

int n;
cin >> n;
int array[n];

Esa sintaxis es para declarar un array de manera estática, y se debe conocer el tamaño en tiempo de compilación, no de ejecución. Tu lo que quieres hacer según he entendido es esto:

int n;
cin >> n;
int* array = new int[n];

En este caso si se declarara dinamicamente y por lo tanto puedes conocer el tamaño del vector en tiempo de ejecución. Y no, no te puedes olvidar del delete, el lenguaje se pensó para eso. A no ser que uses algo ya preparado como los arrays de la libreria estandar, que ya se ocupan ellos por dentro del manejo de su memoria. Sobre si es mejor usar la libreria estandar o hacerlo a mano, siempre es mejor evitar reinventar la rueda. Puedes fiarte de la libreria estandar al 99%. Pero en tu caso que estas aprendiendo es mejor que hagas las cosas manualmente, al menos al principio. Por último, siempre que puedas es mejor declarar las cosas de manera estatica, dependiendo del contexto donde estes programando, pero en general si conoces el maximo de elementos posibles de dicha lista y dices que no pueden ingresar un numero mas alto que ese, hazlo estatico.

Edit: El primer caso que he puesto no da error a no ser que estes compilando con -Wall -Werror, los VLA parece que se permiten desde hace tiempo pero se desaconseja su uso. Juraría que esto me dio fallo en su momento, curioso.

crb2222

No te sé responder porque solo di C en la universidad y hace siglos de ellos, pero… porque estas aprendiendo cpp?
Osea, para cálculo científico y en general para la vida, aprende python. Te ahorraras dolores de cabeza en parafernalia propia del lenguaje que no te aportan nada, a ti ni al 99% de los casos donde estos lenguajes no son necesarios

1 respuesta
Kike_Knoxvil

#8 Te sorprendería la cantidad de C/C++ que se utiliza a nivel industrial

nobody1

El creador del c++ desaconseja utilizar new y delete, sólo usarlo cuando sea estrictamente necesario, para todo lo demás usar "std::vector".

Puedes buscar en google la conferencia en la que lo dice, o en cualquiera de sus libros.

overflow

Si entiendo bien el tema está en el tipo de dato que quieras almacenar... mientras sean de tamaño fijo todos contentos y mejor indicar el tamaño que vas a usar de antemano. Al final cuanto más predecible sea un programa mejor... el tema de definir un array de tamaño fijo con datos de tamaño variable es que en cada inserción va a tener que hacer una operación para acomodarse al nuevo elemento agregado.

Slowbro

#1

Tómatelo con calma que la gestión de memoria en general es compleja y en particular, C++ te permite hacer demasiadas cosas. Si estás siguiendo un libro, te aconsejo que no te atasques con algo y sigas hasta el final, ya que lo más seguro es que el autor quiera introducirte al problema de como no liarla al liberar memoria, dándote ejemplos bastante alejados del entorno real.

Como ya han comentado, en un entorno real, lo más común sería utilizar las estructuras y algoritmos de std (vector para tu caso) , con smart pointers y dentro de una clase tuya.

Btw, fijate en lo que espera un array:

An integral constant expression (until C++14), a converted constant expression of type std::size_t (since C++14), which evaluates to a value greater than zero

Fuente

Disclaimer: Pico poco C++, y en el 80% de los casos no puedo usar heap (malloc/new).

PD: Si te interesa la parte de scicomp, https://github.com/cpp-linear-algebra/cpp-linear-algebra-brainstorming/wiki/Prior-Art

willy_chaos

yo he trabajado años con java y este año estaba portando un 'script' de python a c++ y joder... lo hice yo en python , pasarlo a java seria easy... pues en c++ me siento totalmente noob.. cada dos por tres mirando docu jajajaja

Glumyglu

#5 eso me imaginaba yo y es lo que me dijeron con python, el problema es que leyendo el libro que cito dicen que es más eficiente ir llenándolo, cosa que no he leído en ningún otro lado. Por eso también quería preguntarlo.

#6 Genial, gracias.

#7 me da un type error porque la dimensión tiene que conocerse en compile time.

#8 Quiero cursar una asignatura de computación paralela el año que viene y recomiendan Java o Cpp, escogí Cpp porque suelo ver que lo piden más, al menos a nivel de PHD.

#10 #11 Gracias.

#12 yo creo que lo hacen porque es un libro antiguo y std::vector o no existía o no se actualizaron para usarlo, porque te dicenq ue es la única solución y hasta se crean sus propias clases de vectores.

1 respuesta
refresco

#14 algun motivo mas para quedarte con mpi y ese libro tan antiguo y no con cuda y openacc ? yo cuando me puse con el tenia mucha mas documentacion y mas actualizada

1 respuesta
Glumyglu

#15 la parte de computación paralela me la estoy saltando. Sigo el libro porque quiero repasar métodos numéricos básicos a la vez que pongo en práctica lo que aprendo de C++ por otros libros.

cabron

C++ ha cambiado mucho, pero una burrada, en los últimos años. Desde el 2003 que es de cuando dices que es ese libro se han hecho estas revisiones:

C++11,
C++14,
C++17,
C++20

Y no son precisamente cambios triviales, algunos han sido bastante gordos.

Te recomiendo que al menos lo complementes con algo más actualizado

Usuarios habituales

  • Glumyglu
  • refresco
  • Slowbro
  • overflow
  • nobody1
  • Kike_Knoxvil
  • shaba