Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: xaps en 21 Noviembre 2013, 01:21 am



Título: [SOLUCIONADO] C++ - Error con memoria dinámica: ¿Puntero por referencia?
Publicado por: xaps en 21 Noviembre 2013, 01:21 am
Dadas dos funciones, necesito pasar un puntero por referencia de una función a la otra para obtener los datos de una lista de strings (las dos funciones están dentro de la misma clase).
El problema viene dado a que desde un objeto de esa clase, necesito acceder a datos privados de otro objeto del mismo tipo (una lista de strings). Entonces, la solución más eficiente que se me ha ocurrido es crear un puntero de tipo vector<string> vect, pasarlo por referencia a la otra función, y en esa hacer vect = new vector<string> (N), donde N es el tamaño de la lista de la que quiero obtener sus elementos, pero me da errores de compilación y no consigo resolverlos.

Os adjunto el código de las dos funciones implicadas y el error recibido al compilar (compilo en Linux con G++):

NOTA: La primera función empieza en la línea 33, y la segunda en la línea 71, por lo que los errores saltan en las líneas marcadas con el comentario ERROR EN ESTA LÍNEA.

Código
  1. #include <vector>
  2. #include <list>
  3.  
  4. list<string> lista; //Esto está declarado en la parte privada de la clase
  5.  
  6. void Objeto::fusion_objeto(const Objeto &rev)
  7. {
  8.  //Declaro puntero
  9.  vector<string> *s = NULL;
  10.  rev.func_aux(s);
  11.  int n = s->size();
  12.  for (int i = 0; i < n; ++i) lista.insert(lista.begin(), s[i]); //ERROR EN ESTA LÍNEA
  13.  delete s;
  14. }
  15.  
  16. void Objeto::func_aux(vector<string>* &s)
  17. {
  18.  int N = lista.size();
  19.  s = new vector<string> (N);
  20.  list<string>::iterator it = lista.begin();
  21.  for (int i = 0; i < N; ++i)
  22.  {
  23.    s[i] = *it; //ERROR EN ESTA LÍNEA
  24.    ++it;
  25.  }
  26. }
  27.  

Error de la primera función:
Código:
Objeto.cpp: In member function ‘void Objeto::fusion_objeto(const Objeto&)’:
Objeto.cpp:39:69: error: no matching function for call to ‘std::list<std::basic_string<char> >::insert(std::list<std::basic_string<char> >::iterator, std::vector<std::basic_string<char> >&)’
Objeto.cpp:39:69: note: candidates are:
In file included from /usr/include/c++/4.7/list:65:0,
                 from Objeto.hpp:8,
                 from Objeto.cpp:1:
/usr/include/c++/4.7/bits/list.tcc:99:5: note: std::list<_Tp, _Alloc>::iterator std::list<_Tp, _Alloc>::insert(std::list<_Tp, _Alloc>::iterator, const value_type&) [with _Tp = std::basic_string<char>; _Alloc = std::allocator<std::basic_string<char> >; std::list<_Tp, _Alloc>::iterator = std::_List_iterator<std::basic_string<char> >; std::list<_Tp, _Alloc>::value_type = std::basic_string<char>]
/usr/include/c++/4.7/bits/list.tcc:99:5: note:   no known conversion for argument 2 from ‘std::vector<std::basic_string<char> >’ to ‘const value_type& {aka const std::basic_string<char>&}’
In file included from /usr/include/c++/4.7/list:64:0,
                 from Objeto.hpp:8,
                 from Objeto.cpp:1:
/usr/include/c++/4.7/bits/stl_list.h:1104:7: note: void std::list<_Tp, _Alloc>::insert(std::list<_Tp, _Alloc>::iterator, std::list<_Tp, _Alloc>::size_type, const value_type&) [with _Tp = std::basic_string<char>; _Alloc = std::allocator<std::basic_string<char> >; std::list<_Tp, _Alloc>::iterator = std::_List_iterator<std::basic_string<char> >; std::list<_Tp, _Alloc>::size_type = long unsigned int; std::list<_Tp, _Alloc>::value_type = std::basic_string<char>]
/usr/include/c++/4.7/bits/stl_list.h:1104:7: note:   candidate expects 3 arguments, 2 provided
/usr/include/c++/4.7/bits/stl_list.h:1125:9: note: template<class _InputIterator> void std::list::insert(std::list<_Tp, _Alloc>::iterator, _InputIterator, _InputIterator) [with _InputIterator = _InputIterator; _Tp = std::basic_string<char>; _Alloc = std::allocator<std::basic_string<char> >]
/usr/include/c++/4.7/bits/stl_list.h:1125:9: note:   template argument deduction/substitution failed:
Objeto.cpp:39:69: note:   candidate expects 3 arguments, 2 provided


Error de la segunda función:
Código:
Objeto.cpp: In member function ‘void Objeto::func_aux(std::vector<std::basic_string<char> >*&)’:
Objeto.cpp:78:13: error: no match for ‘operator=’ in ‘*(s + ((sizetype)(((long unsigned int)i) * 24ul))) = it.std::_List_iterator<_Tp>::operator*<std::basic_string<char> >()’
Objeto.cpp:78:13: note: candidate is:
In file included from /usr/include/c++/4.7/vector:70:0,
                 from Objeto.hpp:9,
                 from Objeto.cpp:1:
/usr/include/c++/4.7/bits/vector.tcc:161:5: note: std::vector<_Tp, _Alloc>& std::vector<_Tp, _Alloc>::operator=(const std::vector<_Tp, _Alloc>&) [with _Tp = std::basic_string<char>; _Alloc = std::allocator<std::basic_string<char> >]
/usr/include/c++/4.7/bits/vector.tcc:161:5: note:   no known conversion for argument 1 from ‘std::basic_string<char>’ to ‘const std::vector<std::basic_string<char> >&’

Tiene pinta de que acceda mal a las posiciones del vector dinámico. He probado con lo siguiente, pero lógicamente no funciona.
Código:
*(s[i])

¿Alguna idea?

Saludos y gracias


Título: Re: C++ - Error con memoria dinámica: ¿Puntero por referencia?
Publicado por: amchacon en 21 Noviembre 2013, 12:15 pm
Muy abstracto *_*

Citar
El problema viene dado a que desde un objeto de esa clase, necesito acceder a datos privados de otro objeto del mismo tipo (una lista de strings). Entonces, la solución más eficiente que se me ha ocurrido es crear un puntero de tipo vector<string> vect, pasarlo por referencia a la otra función
C++ te permite el uso de variables "referencia". Son más seguras y más naturales que los punteros:

Código
  1. int cosa = 0;
  2. int &objeto = cosa;
  3.  
  4. cout<<objeto<<endl;
  5.  
  6. cosa = 1;
  7.  
  8. cout<<objeto<<endl;

No entiendo bien tú código pero un vector se puede copiar con el constructor copia:

Código
  1. vector<string> tuVector(otroVector);

Si usas un puntero, tendrías que poner eso en el new. Pero repito que no necesitas punteros para nada.


Por otro lado si quieres insertar un elemento al principio, te vendría mejor push_front:
http://www.cplusplus.com/reference/list/list/push_front/




Título: Re: C++ - Error con memoria dinámica: ¿Puntero por referencia?
Publicado por: xaps en 21 Noviembre 2013, 13:47 pm
Muy abstracto *_*
C++ te permite el uso de variables "referencia". Son más seguras y más naturales que los punteros:

Código
  1. int cosa = 0;
  2. int &objeto = cosa;
  3.  
  4. cout<<objeto<<endl;
  5.  
  6. cosa = 1;
  7.  
  8. cout<<objeto<<endl;

...

Por otro lado si quieres insertar un elemento al principio, te vendría mejor push_front:
http://www.cplusplus.com/reference/list/list/push_front/

Al ser parte de un trabajo de la universidad, tengo varias restricciones. Entre ellas, están no poder hacer funciones que retornen tipos complejos (como lo son vectores o listas), no poder usar ciertos métodos (push_front lo tenemos prohibido tanto en listas como vectores) y el uso de & lo tengo restringido para pasar parametros por referencia en las funciones. Los métodos no deberian cambiar por temas de restricciones como ya he dicho. Lo ideal sería encontrar el problema con el puntero sin cambiar la estructura de las funciones.

No entiendo bien tú código pero un vector se puede copiar con el constructor copia:

Código
  1. vector<string> tuVector(otroVector);

Si usas un puntero, tendrías que poner eso en el new. Pero repito que no necesitas punteros para nada.

Estoy intentando pasar el contenido de una lista a un vector, por lo que el uso del constructor-copia de <vector> no serviría para este caso.

Si hay algo que no entiendes de mi código coméntalo y intentaré explicártelo lo mejor que pueda. Lo mejor para estos temas es que todos los que nos involucremos aprendamos algo nuevo.

Saludos


Título: Re: C++ - Error con memoria dinámica: ¿Puntero por referencia?
Publicado por: lapras en 21 Noviembre 2013, 14:16 pm
El segundo argumento de lista.insert() debería ser un número.
Además de eso, dado que s es un puntero debes acceder los elementos así:
Código
  1. (*s)[i]
en vez de así:
Código
  1. s[i]
Otra cosa es que no tiene sentido pasar un puntero por referencia:
Código
  1. vector<string>* &s


Título: Re: C++ - Error con memoria dinámica: ¿Puntero por referencia?
Publicado por: xaps en 21 Noviembre 2013, 15:19 pm
El segundo argumento de lista.insert() debería ser un número.
Además de eso, dado que s es un puntero debes acceder los elementos así:
Código
  1. (*s)[i]
en vez de así:
Código
  1. s[i]
Otra cosa es que no tiene sentido pasar un puntero por referencia:
Código
  1. vector<string>* &s

El segundo parametro del insert no debería ser un número, ya que la lista se ha declarado como lista de string.

En cuanto a la solución dada, funciona, muchas gracias. No sé como no se me había pasado por la cabeza esto... Supongo que por la poca experiencia que tengo en memoria dinámica.

Una duda:
Código:
*s[i]
Esto no me funcionaba porque lo cogía todo como un puntero, ¿verdad? Es decir, como si fuera un vector de punteros.

Y la segunda parte en la que me dices que no tiene sentido pasar un puntero por referencia, supongo que es porque aunque se cree una copia del puntero, la posición de memoria a la que apunta será modificada igualmente, ¿cierto? Por lo que el puntero de la primera función ya apuntará al valor modificado.

Saludos


Título: Re: C++ - Error con memoria dinámica: ¿Puntero por referencia?
Publicado por: lapras en 21 Noviembre 2013, 23:05 pm
Tienes razón en lo del insert, fallo mío.

Lo del puntero es por el tema de las precedencias, el operador [] es el que tiene más precedencia junto con otros. Entonces esto *s es equivalente a esto *(s).
Como bien has dicho, lo que haces es coger un elemento e intentas usarlo como si fuese un puntero.

Lo de pasar un puntero por referencia lo digo porque los parametros que se pasan por refrencia suelen ser estructuras de tamaño considerable.  Eso es porque copiar dicha estructura sería muy costoso. En realidad pasar algo por referencia es como pasar un puntero a ese algo, sólo que dentro de la función no necesitas hacer tantos castings(queda feo tanto paréntesis), y además al llamar a la función pasas el parametro como si fuese por valor(te ahorras el &). Volviendo a lo de pasar un puntero por referencia, un puntero ocupa poco, no necesitas pasar una refencia a un puntero(es como si pasas un puntero a un puntero). No esta mal aunque si lo pasas por valor ahorras un acceso a memoria.


Título: Re: C++ - Error con memoria dinámica: ¿Puntero por referencia?
Publicado por: xaps en 22 Noviembre 2013, 02:32 am
Tienes razón en lo del insert, fallo mío.

Lo del puntero es por el tema de las precedencias, el operador [] es el que tiene más precedencia junto con otros. Entonces esto *s es equivalente a esto *(s).
Como bien has dicho, lo que haces es coger un elemento e intentas usarlo como si fuese un puntero.

Lo de pasar un puntero por referencia lo digo porque los parametros que se pasan por refrencia suelen ser estructuras de tamaño considerable.  Eso es porque copiar dicha estructura sería muy costoso. En realidad pasar algo por referencia es como pasar un puntero a ese algo, sólo que dentro de la función no necesitas hacer tantos castings(queda feo tanto paréntesis), y además al llamar a la función pasas el parametro como si fuese por valor(te ahorras el &). Volviendo a lo de pasar un puntero por referencia, un puntero ocupa poco, no necesitas pasar una refencia a un puntero(es como si pasas un puntero a un puntero). No esta mal aunque si lo pasas por valor ahorras un acceso a memoria.

Muchas gracias por la explicación, me has resuelto la duda.

Saludos


Título: Re: C++ - Error con memoria dinámica: ¿Puntero por referencia?
Publicado por: lapras en 22 Noviembre 2013, 17:16 pm
Muchas gracias por la explicación, me has resuelto la duda.

Saludos

Me alegro  ;)