2011-01-31 11 views
6

Me gustaría utilizar un estándar :: vector para controlar una pieza determinada de memoria. En primer lugar, estoy bastante seguro de que no es una buena práctica, pero la curiosidad me ha superado y me gustaría saber cómo hacer esto de todos modos.contrucción de vector de C++ con memoria dada

El problema que tengo es un método como este:

vector<float> getRow(unsigned long rowIndex) 
{ 
    float* row = _m->getRow(rowIndex); // row is now a piece of memory (of a known size) that I control 
    vector<float> returnValue(row, row+_m->cols()); // construct a new vec from this data 
    delete [] row; // delete the original memory 
    return returnValue; // return the new vector 
} 

_m es una clase de interfaz de DLL que devuelve una matriz de flotador que es la responsabilidad de las personas que llaman a eliminar. Así que me gustaría envolver esto en un vector y devolverlo al usuario ... pero esta implementación asigna nueva memoria para el vector, la copia, y luego elimina la memoria devuelta, luego devuelve el vector.

Lo que me gustaría hacer es decir directamente al nuevo vector que tiene control total sobre este bloque de memoria para que cuando se elimine se limpie la memoria.

ACTUALIZACIÓN: La motivación original para este (memoria de regresar de una DLL) ha sido bastante firmemente aplastada por un número de respondedores :) Sin embargo, me gustaría saber la respuesta a la pregunta de todos modos ... Es Hay una forma de construir un std :: vector usando un trozo dado de memoria preasignada T * array, y el tamaño de esta memoria?

+1

El código publicado provoca un comportamiento indefinido cuando se llama a 'eliminar fila' si la memoria se asignó con' nuevo [] 'o' malloc' –

+0

No creo que pueda darle un 'vector' a un pedazo de memoria porque 'vector' necesita ser capaz de asignar y desasignar a voluntad a medida que cambia la capacidad del' vector'. En esta situación, eso requiere conocimiento de cómo su DLL asigna/desasigna memoria, que por supuesto no debería ser información requerida para trabajar con una interfaz DLL adecuada. –

+0

@ChrisDodd, el error está corregido. @Insilico No me importa si desasigna y reasigna esta memoria ... Solo quiero poder decir "Comience a usar esta pieza de memoria" –

Respuesta

4

El asignador predeterminado de vectores no proporciona este tipo de acceso a sus partes internas. Podrías hacerlo con tu propio asignador (el segundo parámetro de plantilla del vector), pero eso cambiaría el tipo del vector.

Sería mucho más fácil si se puede escribir directamente en el vector:

vector<float> getRow(unsigned long rowIndex) { 
    vector<float> row (_m->cols()); 
    _m->getRow(rowIndex, &row[0]); // writes _m->cols() values into &row[0] 
    return row; 
} 

Tenga en cuenta que & fila [0] es un flotador * y se garantiza para el vector para almacenar artículos de forma contigua.

+0

Lo ideal sería hacerlo sin cambiar el tipo. El objetivo es proporcionar a la persona que llama de getRow() un vector estándar (pero sin la asignación de memoria adicional) –

+1

+1 Si el OP tiene acceso a la fuente DLL, esta es la mejor manera de hacerlo. –

+0

En este caso, puedo cambiar el origen de la DLL (y muy probablemente lo haga). Sin embargo, estoy realmente interesado en saber si es posible hacerlo sin el cambio. –

0

Su mejor opción es probablemente una std::vector<shared_ptr<MatrixCelType>>.

Muchos más detalles en this thread.

+2

Dios no ...deja de tirar 'shared_ptr' en todas partes, por favor:/ –

4

La respuesta obvia es utilizar un asignador personalizado, sin embargo, es posible que encuentre que es una solución bastante pesada para lo que necesita. Si desea hacerlo, la forma más simple es tomar el asignador definido (como el argumento de la plantilla de segundo predeterminado para el vector <>) por la implementación, copiar eso y hacerlo funcionar según sea necesario.

Otra solución podría ser definir una plantilla de especialización de vector, definir la mayor parte de la interfaz que realmente necesita e implementar la personalización de memoria.

Finalmente, ¿qué hay de definir su propio contenedor con una interfaz STL conforme, definiendo iteradores de acceso aleatorio, etc. Esto podría ser bastante fácil dado que la matriz subyacente se correlacionará bien con el vector <>, y los punteros en ella se correlacionarán con iteradores.

Opina sobre ACTUALIZACIÓN: "¿Hay alguna manera de construir un std :: vector usando un trozo dado de memoria preasignada T * y el tamaño de esta memoria?"

Seguramente la respuesta simple aquí es "No". Si desea que el resultado sea un vector <>, debe admitir el crecimiento según sea necesario, como por ejemplo a través del método reserve(), y eso no será posible para una asignación fija determinada. Entonces, la verdadera pregunta es realmente: ¿qué es exactamente lo que quieres lograr? Algo que se puede usar como vector <>, o algo que realmente tiene que ser en cierto sentido ser un vector, y si es así, ¿cuál es ese sentido?

0

Si está tratando de cambiar dónde/cómo el vector asigna/reasigna/desasigna la memoria, el parámetro de la plantilla del asignador de la clase vectorial es lo que está buscando.

Si simplemente está tratando de evitar la sobrecarga de la construcción, copie la construcción, la asignación y la destrucción, luego permita al usuario crear una instancia del vector y luego páselo a su función por referencia. El usuario es responsable de la construcción y la destrucción.

Parece que lo que estás buscando es una forma de puntero inteligente. Uno que elimina lo que apunta cuando se destruye. Mire en las bibliotecas de Boost o haga las suyas propias en ese caso.

2

Lo más importante que debe saber aquí es que diferentes DLL/Módulos tienen diferentes Heaps. Esto significa que cualquier memoria que se asigna desde una DLL debe eliminarse de esa DLL (no es solo cuestión de la versión del compilador o delete frente a delete[] o lo que sea). NO PASAR RESPONSABILIDAD DE LA ADMINISTRACIÓN DE MEMORIAS A TRAVÉS DE UN LÍMITE DE DLL. Esto incluye crear un std::vector en un dll y devolverlo. Pero también incluye pasar un std::vector a la DLL que debe rellenar el DLL; Tal operación no es segura ya que no está seguro de que el std::vector no intente cambiar el tamaño de algún tipo mientras se está llenando de valores.

Hay dos opciones:

  • definir sus propias allocator para la clase std::vector que utiliza una función de asignación que está garantizado para residir en el DLL/Módulo de la que fue creado el vector. Esto se puede hacer fácilmente con enlace dinámico (es decir, hacer que la clase allocator llame a alguna función virtual). Como la vinculación dinámica buscará en el vtable para la llamada a la función, se garantiza que recaerá en el código de la DLL/Módulo que la creó originalmente.

  • No pase el objeto vectorial hacia o desde la DLL. Puede usar, por ejemplo, una función getRowBegin() y getRowEnd() que devuelven iteradores (es decir, punteros) en la matriz de filas (si es contigua) y le permite al usuario std::copy convertirlo en su propio objeto local std::vector. También podría hacerlo al revés, pase los iteradores begin() y end() a una función como fillRowInto(begin, end).

Este problema es muy real, aunque muchas personas lo descuidan sin saberlo. No lo subestimes. Personalmente he sufrido errores silenciosos relacionados con este problema y no era bonito. Me llevó meses resolverlo.

He comprobado el código fuente, y boost::shared_ptr y boost::shared_array utilizan el enlace dinámico (primera opción anterior) para tratar esto ... sin embargo, no se garantiza que sean compatibles con binarios. Aún así, esta podría ser una opción un poco mejor (por lo general, la compatibilidad binaria es un problema mucho menor que la gestión de memoria en los módulos).

+0

"diferentes DLL/Módulos tienen diferentes Heaps": esto solo es cierto si cambia la configuración de compilación de su valor predeterminado (o compila con varios compiladores, lo que es aún más complejo). Con la configuración predeterminada ('/ MD') solo hay un montón. – MSalters

0

La biblioteca Boost.SmartPtr contiene una gran cantidad de clases interesantes, algunas de las cuales están dedicadas a manejar arreglos.

Por ejemplo, he aquí scoped_array:

int main(int argc, char* argv[]) 
{ 
    boost::scoped_array<float> array(_m->getRow(atoi(argv[1]))); 
    return 0; 
} 

El problema, por supuesto, es que scoped_array no puede ser copiado, así que si usted realmente quiere un std::vector<float>, @Fred Nurk es probablemente lo mejor que se puede conseguir.

En el caso ideal, desea el equivalente a unique_ptr pero en forma de matriz, sin embargo, no creo que sea parte del estándar.

Cuestiones relacionadas