Foro de elhacker.net

Programación => Programación C/C++ => Mensaje iniciado por: xaps en 13 Diciembre 2013, 18:50 pm



Título: C++ - Problema con operador delete
Publicado por: xaps en 13 Diciembre 2013, 18:50 pm
Estoy desarrollando una clase Matriz, en la que le he incluido varias sobrecargas de operadores para poder trabajar con estas matrices de una manera más cómoda. El problema viene dado cuando entra en juego el operador de asignación, cuando usa la operación delete para liberar la memoria de la matriz y crear una copia del parámetro, que me da el siguiente error, además de un mapeado de memoria:
Código:
*** glibc detected *** ./test.exe: free(): invalid pointer: 0x0000000000401662 ***

Os adjunto el código de la clase y el código de test.exe:

Matriz.h
Código
  1. #include <iostream>
  2. #include <vector>
  3.  
  4. using namespace std;
  5.  
  6. class Matriz {
  7.  
  8. private:
  9.  
  10.  int nfilas;
  11.  int ncolumnas;
  12.  int **matr;
  13.  
  14. public:
  15.  
  16.  //Constructoras
  17.  Matriz();
  18.  Matriz(int filas, int columnas);
  19.  
  20.  //Destructora
  21.  ~Matriz();
  22.  
  23.  //Consultoras
  24.  int filas() const;
  25.  int columnas() const;
  26.  int consultar(int fila, int columna) const;
  27.  
  28.  //Modificadora
  29.  void modificar(int fila, int columna, int x);
  30.  
  31.  //Entrada / Salida
  32.  void leer();
  33.  void escribir();
  34.  
  35.  //Operadores
  36.  Matriz operator +(const Matriz &b) const;
  37.  Matriz operator -(const Matriz &b) const;
  38.  Matriz operator *(const Matriz &b) const;
  39.  Matriz operator =(const Matriz &b);
  40.  
  41.  bool operator ==(const Matriz &mat) const;
  42.  bool operator !=(const Matriz &mat) const;
  43. };
  44.  
  45. Matriz::Matriz() {}
  46.  
  47. Matriz::Matriz(int filas, int columnas)
  48. {
  49.  this->nfilas = filas;
  50.  this->ncolumnas = columnas;
  51.  
  52.  matr = new int* [filas];
  53.  for (int i = 0; i < filas; ++i)
  54.  {
  55.    matr[i] = new int [columnas];
  56.  }
  57. }
  58.  
  59. Matriz::~Matriz()
  60. {
  61.  delete matr;
  62. }
  63.  
  64. int Matriz::filas() const
  65. {
  66.  return nfilas;
  67. }
  68.  
  69. int Matriz::columnas() const
  70. {
  71.  return ncolumnas;
  72. }
  73.  
  74. int Matriz::consultar(int fila, int columna) const
  75. {
  76.  return matr[fila][columna];
  77. }
  78.  
  79. void Matriz::modificar(int fila, int columna, int x)
  80. {
  81.  matr[fila][columna] = x;
  82. }
  83.  
  84. void Matriz::leer()
  85. {
  86.  for (int i = 0; i < nfilas; ++i)
  87.    for (int j = 0; j < ncolumnas; ++j) cin >> matr[i][j];
  88. }
  89.  
  90. void Matriz::escribir()
  91. {
  92.  for (int i = 0; i < nfilas; ++i)
  93.  {
  94.    for (int j = 0; j < ncolumnas; ++j) cout << matr[i][j] << " ";
  95.    cout << endl;
  96.  }
  97. }
  98.  
  99. Matriz Matriz::operator +(const Matriz &b) const
  100. {
  101.  int filas = this->nfilas;
  102.  int columnas = this->ncolumnas;
  103.  
  104.  Matriz res(filas, columnas);
  105.  
  106.  for (int i = 0; i < filas; ++i)
  107.    for (int j = 0; j < columnas; ++j)
  108.      res.matr[i][j] = this->matr[i][j] + b.matr[i][j];
  109.  
  110.  return res;
  111. }
  112.  
  113. Matriz Matriz::operator -(const Matriz &b) const
  114. {
  115.  int filas = this->nfilas;
  116.  int columnas = this->ncolumnas;
  117.  
  118.  Matriz res(filas, columnas);
  119.  
  120.  for (int i = 0; i < filas; ++i)
  121.    for (int j = 0; j < columnas; ++j)
  122.      res.matr[i][j] = this->matr[i][j] - b.matr[i][j];
  123.  
  124.  return res;
  125. }
  126.  
  127. Matriz Matriz::operator *(const Matriz &b) const
  128. {
  129.  if (this->ncolumnas == b.nfilas)
  130.  {
  131.    int pos = this->ncolumnas;
  132.    int filas = this->nfilas;
  133.    int columnas = b.ncolumnas;
  134.  
  135.    Matriz res(filas, columnas);
  136.    for (int i = 0; i < filas; ++i)
  137.    {
  138.      for (int j = 0; j < columnas; ++j)
  139.      {
  140. int value = 0;
  141. for (int k = 0; k < pos; ++k) value += this->matr[i][k] * b.matr[k][j];
  142. res.matr[i][j] = value;
  143.      }
  144.    };
  145.    return res;
  146.  }
  147. }
  148.  
  149. Matriz Matriz::operator =(const Matriz &mat)
  150. {
  151.  if (this != &mat)
  152.  {
  153.    cout << "flag1" << endl;
  154.    delete this->matr;
  155.    cout << "flag2" << endl;
  156.    if (mat.matr)
  157.    {
  158.      this->nfilas = mat.nfilas;
  159.      this->ncolumnas = mat.ncolumnas;
  160.  
  161.      matr = new int* [nfilas];
  162.      for (int i = 0; i < nfilas; ++i)
  163.      {
  164. matr[i] = new int [ncolumnas];
  165.      }
  166.  
  167.      for (int i = 0; i < nfilas; ++i)
  168.      {
  169. for (int j = 0; j < ncolumnas; ++j)
  170. {
  171.  matr[i][j] = mat.matr[i][j];
  172. }
  173.      }
  174.    }
  175.    else this->matr = NULL;
  176.  }
  177.  return *this;
  178. }
  179.  
  180. bool Matriz::operator ==(const Matriz &mat) const
  181. {
  182.  for (int i = 0; i < nfilas; ++i)
  183.    for (int j = 0; j < ncolumnas; ++j)
  184.      if (this->matr[i][j] != mat.matr[i][j]) return false;
  185.  
  186.  return true;
  187. }
  188.  
  189. bool Matriz::operator !=(const Matriz &mat) const
  190. {
  191.  for (int i = 0; i < nfilas; ++i)
  192.    for (int j = 0; j < ncolumnas; ++j)
  193.      if (this->matr[i][j] != mat.matr[i][j]) return true;
  194.  
  195.  return false;
  196. }
  197.  

Código
  1. #include "Matriz.h"
  2.  
  3. int main()
  4. {
  5.  int f1, c1, f2, c2;
  6.  
  7.  cout << "Introduce el tamaño de la primera matriz:" << endl;
  8.  cin >> f1 >> c1;
  9.  Matriz mat1(f1, c1);
  10.  cout << "Introduce los valores de la matriz:" << endl;
  11.  mat1.leer();
  12.  
  13.  cout << "Introduce el tamaño de la segunda matriz:" << endl;
  14.  cin >> f2 >> c2;
  15.  Matriz mat2(f2, c2);
  16.  cout << "Introduce los valores de la matriz:" << endl;
  17.  mat2.leer();
  18.  
  19.  
  20.  if (c1 == f2)
  21.  {
  22.    Matriz res;
  23.    res = mat1 * mat2;
  24.  
  25.    res.escribir();
  26.  }
  27. }
  28.  

He probado varias cosas, como cambiar delete por delete[], o intentar eliminar los vectores individualmente, pero ninguna ha dado resultado. Algo debo estar haciendo mal, pero no encuentro el error. Si le pudierais echar un ojo me haríais un favor.

Muchas gracias.


Título: Re: C++ - Problema con operador delete
Publicado por: amchacon en 13 Diciembre 2013, 22:39 pm
Código
  1. Matriz::~Matriz()
  2. {
  3. delete matr;
  4. }
Eso está mal hecho, tienes que borrar al contrario que como la creas (primero con un for borras todos los vectores y despues borras el puntero principal).

Código
  1. Matriz::~Matriz()
  2. {
  3. for (int i = 0; i < nfilas;i++) delete[] matr[i];
  4. delete[] matr;
  5. }


Título: Re: C++ - Problema con operador delete
Publicado por: xaps en 14 Diciembre 2013, 12:43 pm
Código
  1. Matriz::~Matriz()
  2. {
  3. delete matr;
  4. }
Eso está mal hecho, tienes que borrar al contrario que como la creas (primero con un for borras todos los vectores y despues borras el puntero principal).

Código
  1. Matriz::~Matriz()
  2. {
  3. for (int i = 0; i < nfilas;i++) delete[] matr[i];
  4. delete[] matr;
  5. }

Gracias por la explicación. Eso es a lo que me refería cuando decía de eliminar los vectores individualmente. No me daba resultado por un error en el código de la sobrecarga del operador de asignación, en el que me pongo a eliminar los datos de matr sin verificar que esté vacío. Pero ahora, una vez arreglado ésto, me ocurre una cosa bastante curiosa que me produce un error de segmentación. Primero adjunto las funciones que he arreglado, y luego comento cual es el error que estoy teniendo ahora:

~Matriz();
Código
  1. Matriz::~Matriz()
  2. {
  3.  for (int i = 0; i < this->nfilas; ++i)
  4.  {
  5.    cout << "~fila " << i << endl;
  6.    delete[] this->matr[i];
  7.  }
  8.  delete[] this->matr;
  9. }

Matriz operator =(const Matriz &mat);
Código
  1. Matriz Matriz::operator =(const Matriz &mat)
  2. {
  3.  cout << "asignacion" << endl;
  4.  if (this != &mat)
  5.  {
  6.    if (this != NULL)
  7.    {
  8.      cout << "flag1" << endl;
  9.  
  10.      for (int i = 0; i < this->nfilas; ++i)
  11.      {
  12. cout << "fila " << i << endl;
  13. delete[] this->matr[i];
  14.      }
  15.      delete[] this->matr;
  16.    }
  17.  
  18.    cout << "flag2" << endl;
  19.    if (mat.matr)
  20.    {
  21.      this->nfilas = mat.nfilas;
  22.      this->ncolumnas = mat.ncolumnas;
  23.  
  24.      matr = new int* [nfilas];
  25.      for (int i = 0; i < nfilas; ++i)
  26.      {
  27. matr[i] = new int [ncolumnas];
  28.      }
  29.  
  30.      for (int i = 0; i < nfilas; ++i)
  31.      {
  32. for (int j = 0; j < ncolumnas; ++j)
  33. {
  34.  matr[i][j] = mat.matr[i][j];
  35. }
  36.      }
  37.    }
  38.    else this->matr = NULL;
  39.  }
  40.  return *this;
  41. }

Pues bien, el error es el siguiente: Parece que justo antes de realizar la asignación el destructor por defecto se carga la matriz, y cuando en el delete de la sobrecarga de asignación intenta acceder a la posición "i" le da un error de segmentación. He llegado a esta conclusión mediante la salida que me ha dado el programa. Os enseño cual es la entrada que le he dado y cual es la salida que me ha devuelto:

Entrada:
Código:
2 2
1 1 0 1
2 2
1 0 1 1

Salida:
Código:
~fila 0
~fila 1
asignacion
flag1
fila 0
Violación de segmento

Como podéis comprobar, primero entra al destructor por defecto y a continuación intenta realizar la asignación. El problema es que no se ni que matriz elimina, ni porqué la matriz a la que se le realiza la asignación entra dentro de la condición (teóricamente, la matriz res tiene matr = NULL, ¿no?).

Muchas gracias de nuevo.


Título: Re: C++ - Problema con operador delete
Publicado por: amchacon en 14 Diciembre 2013, 13:18 pm
Código
  1. if (this != NULL)

Esa condición siempre va a ser cierta xD. Creo que te referias a this.matr, supongo que de ahí viene el error de segmentación (borrar cuando ni siquiera tienes matriz).

Por cierto no me parece adecuado que copypastees el destructor ahí, yo me haría una función privada "borrar". Y esa función la llamo desde el destructor y desde allí, por reciclar código más que nada.


Título: Re: C++ - Problema con operador delete
Publicado por: xaps en 14 Diciembre 2013, 14:32 pm
Código
  1. if (this != NULL)

Esa condición siempre va a ser cierta xD. Creo que te referias a this.matr, supongo que de ahí viene el error de segmentación (borrar cuando ni siquiera tienes matriz).

Por cierto no me parece adecuado que copypastees el destructor ahí, yo me haría una función privada "borrar". Y esa función la llamo desde el destructor y desde allí, por reciclar código más que nada.

Vaya error mas tonto... jajaj Gracias!

Sobre lo de la función, ni se me había ocurrido. Ya lo he implementado.
Otro "error" que tenia era que en la constructora por defecto no iniciaba matr a NULL, y provocaba que una vez arreglado ese error tan tonto me diera violación de segmento de nuevo ya que aún pasaba la condición.

Pero ahora me vuelve a dar un error de nuevo con la destructora (siento ser tan pesado, pero aún no hemos dado ni memoria dinámica en la universidad y lo estoy haciendo por simple entretenimiento y curiosidad, por lo que estoy investigando por cuenta propia y hay cosas que se me escapan). Primero de todo, volveré a adjuntar la clase tal como la tengo ahora, y a continuación pegaré la salida que me da el programa.

Clase Matriz:
Código
  1. #include <iostream>
  2. #include <vector>
  3.  
  4. using namespace std;
  5.  
  6. class Matriz {
  7.  
  8. private:
  9.  
  10.  int nfilas;
  11.  int ncolumnas;
  12.  int **matr;
  13.  
  14.  void borrar();
  15.  
  16. public:
  17.  
  18.  //Constructoras
  19.  Matriz();
  20.  Matriz(int filas, int columnas);
  21.  
  22.  //Destructora
  23.  ~Matriz();
  24.  
  25.  //Consultoras
  26.  int filas() const;
  27.  int columnas() const;
  28.  int consultar(int fila, int columna) const;
  29.  
  30.  //Modificadora
  31.  void modificar(int fila, int columna, int x);
  32.  
  33.  //Entrada / Sortida
  34.  void leer();
  35.  void escribir();
  36.  
  37.  //Operadores
  38.  Matriz operator +(const Matriz &b) const;
  39.  Matriz operator -(const Matriz &b) const;
  40.  Matriz operator *(const Matriz &b) const;
  41.  Matriz operator =(const Matriz &b);
  42.  
  43.  bool operator ==(const Matriz &mat) const;
  44.  bool operator !=(const Matriz &mat) const;
  45. };
  46.  
  47. Matriz::Matriz() {matr = NULL;}
  48.  
  49. Matriz::Matriz(int filas, int columnas)
  50. {
  51.  this->nfilas = filas;
  52.  this->ncolumnas = columnas;
  53.  
  54.  matr = new int* [filas];
  55.  for (int i = 0; i < filas; ++i)
  56.  {
  57.    matr[i] = new int [columnas];
  58.  }
  59. }
  60.  
  61. Matriz::~Matriz()
  62. {
  63.  cout << "destructora" << endl;
  64.  borrar();
  65. }
  66.  
  67. void Matriz::borrar()
  68. {
  69.  if (this->matr != NULL)
  70.  {
  71.    for (int i = 0; i < nfilas; ++i)
  72.    {
  73.      cout << "fila " << i << endl;
  74.      delete[] matr[i];
  75.    }
  76.    delete[] matr;
  77.    matr = NULL;
  78.  }
  79. }
  80.  
  81. int Matriz::filas() const { return nfilas;}
  82.  
  83. int Matriz::columnas() const { return ncolumnas;}
  84.  
  85. int Matriz::consultar(int fila, int columna) const { return matr[fila][columna];}
  86.  
  87. void Matriz::modificar(int fila, int columna, int x) { matr[fila][columna] = x;}
  88.  
  89. void Matriz::leer()
  90. {
  91.  for (int i = 0; i < nfilas; ++i)
  92.    for (int j = 0; j < ncolumnas; ++j) cin >> matr[i][j];
  93. }
  94.  
  95. void Matriz::escribir()
  96. {
  97.  cout << "flag3" << endl;
  98.  for (int i = 0; i < nfilas; ++i)
  99.  {
  100.    for (int j = 0; j < ncolumnas; ++j) cout << matr[i][j] << " ";
  101.    cout << endl;
  102.  }
  103. }
  104.  
  105. Matriz Matriz::operator +(const Matriz &b) const
  106. {
  107.  int filas = this->nfilas;
  108.  int columnas = this->ncolumnas;
  109.  
  110.  Matriz res(filas, columnas);
  111.  
  112.  for (int i = 0; i < filas; ++i)
  113.    for (int j = 0; j < columnas; ++j)
  114.      res.matr[i][j] = this->matr[i][j] + b.matr[i][j];
  115.  
  116.  return res;
  117. }
  118.  
  119. Matriz Matriz::operator -(const Matriz &b) const
  120. {
  121.  int filas = this->nfilas;
  122.  int columnas = this->ncolumnas;
  123.  
  124.  Matriz res(filas, columnas);
  125.  
  126.  for (int i = 0; i < filas; ++i)
  127.    for (int j = 0; j < columnas; ++j)
  128.      res.matr[i][j] = this->matr[i][j] - b.matr[i][j];
  129.  
  130.  return res;
  131. }
  132.  
  133. Matriz Matriz::operator *(const Matriz &b) const
  134. {
  135.  if (this->ncolumnas == b.nfilas)
  136.  {
  137.    int pos = this->ncolumnas;
  138.    int filas = this->nfilas;
  139.    int columnas = b.ncolumnas;
  140.  
  141.    Matriz res(filas, columnas);
  142.    for (int i = 0; i < filas; ++i)
  143.    {
  144.      for (int j = 0; j < columnas; ++j)
  145.      {
  146. int value = 0;
  147. for (int k = 0; k < pos; ++k) value += this->matr[i][k] * b.matr[k][j];
  148. res.matr[i][j] = value;
  149.      }
  150.    };
  151.    return res;
  152.  }
  153. }
  154.  
  155. Matriz Matriz::operator =(const Matriz &mat)
  156. {
  157.  cout << "asignacion" << endl;
  158.  if (this != &mat)
  159.  {
  160.    cout << "flag1" << endl;
  161.    borrar();
  162.    cout << "flag2" << endl;
  163.  
  164.    if (mat.matr)
  165.    {
  166.      this->nfilas = mat.nfilas;
  167.      this->ncolumnas = mat.ncolumnas;
  168.  
  169.      matr = new int* [nfilas];
  170.      for (int i = 0; i < nfilas; ++i)
  171. matr[i] = new int [ncolumnas];
  172.  
  173.      for (int i = 0; i < nfilas; ++i)
  174.      {
  175. for (int j = 0; j < ncolumnas; ++j)
  176.  matr[i][j] = mat.matr[i][j];
  177.      }
  178.    }
  179.    else this->matr = NULL;
  180.  }
  181.  return *this;
  182. }
  183.  
  184. bool Matriz::operator ==(const Matriz &mat) const
  185. {
  186.  for (int i = 0; i < nfilas; ++i)
  187.    for (int j = 0; j < ncolumnas; ++j)
  188.      if (this->matr[i][j] != mat.matr[i][j]) return false;
  189.  
  190.  return true;
  191. }
  192.  
  193. bool Matriz::operator !=(const Matriz &mat) const
  194. {
  195.  for (int i = 0; i < nfilas; ++i)
  196.    for (int j = 0; j < ncolumnas; ++j)
  197.      if (this->matr[i][j] != mat.matr[i][j]) return true;
  198.  
  199.  return false;
  200. }
  201.  

Los datos de entrada son los mismos de antes, y la salida es la siguiente:
Código:
destructora
fila 0
fila 1
asignacion
flag2
destructora
fila 0
fila 1
destructora
fila 0
*** glibc detected *** ./test.exe: double free or corruption (out): 0x0000000001a590e0 ***
Además del mapeado de memoria que se da con este tipo de error.

Se puede ver como la asignación aparentemente la realiza bien, pero luego, antes de ejecutar la función leer() elimina dos matrices, que sospecho que serán mat1 y mat2 (ya que no se vuelven a usar en el programa) y es cuando salta el mensaje de error. Lo extraño es que ninguna de las dos matrices puede haber sido modificada previamente, ya que el operador de multiplicación y su parámetro son constantes y la asignación trabaja con la matriz resultado de la multiplicación (la que se obtiene del return). Por lo que me lleva a sospechar que la primera matriz que elimina la destructora (la que se elimina antes de hacer la asignación) es la que luego se vuelve a intentar eliminar y provoca el error. Entonces, he pensado en incluir en la función borrar() la misma condición que en la sobrecarga de asignación (y eliminarla de ésta), pero el error sigue ahí (todo esto ya está reflejado en el código de la clase incluido arriba). También he añadido la asignación matr = NULL, aunque no sé si al usar la operación delete un puntero queda con el valor NULL asignado o no.

También he encontrado esto buscando información sobre el delete:
Citar
Cuando se usa el operador delete con un puntero nulo, no se realiza ninguna acción. Esto permite usar el operador delete con punteros sin necesidad de preguntar si es nulo antes.
Fuente: http://c.conclase.net/curso/?cap=013b

Por lo tanto, no debería ser necesaria la condición if (matr != NULL);, ¿no?

¿Alguna idea de que es lo que puede estar ocurriendo?

Muchas gracias!


Título: Re: C++ - Problema con operador delete
Publicado por: amchacon en 14 Diciembre 2013, 15:19 pm
¿Que código has usado para las pruebas?

PD: No sabía lo de los punteros null y delete. Siempre se aprende algo nuevo  :silbar:


Título: Re: C++ - Problema con operador delete
Publicado por: xaps en 14 Diciembre 2013, 16:20 pm
¿Que código has usado para las pruebas?

PD: No sabía lo de los punteros null y delete. Siempre se aprende algo nuevo  :silbar:

El mismo que el del post principal, lo único que he cambiado ha sido la clase.
Te lo adjunto de todas formas:
Código
  1. #include "Matriz.h"
  2.  
  3. int main()
  4. {
  5.  int f1, c1, f2, c2;
  6.  
  7.  cout << "Introduce el tamaño de la primera matriz:" << endl;
  8.  cin >> f1 >> c1;
  9.  Matriz mat1(f1, c1);
  10.  cout << "Introduce los valores de la matriz:" << endl;
  11.  mat1.leer();
  12.  
  13.  cout << "Introduce el tamaño de la segunda matriz:" << endl;
  14.  cin >> f2 >> c2;
  15.  Matriz mat2(f2, c2);
  16.  cout << "Introduce los valores de la matriz:" << endl;
  17.  mat2.leer();
  18.  
  19.  
  20.  if (c1 == f2)
  21.  {
  22.    Matriz res;
  23.    res = mat1 * mat2;
  24.  
  25.    res.escribir();
  26.  }
  27. }
  28.  


Título: Re: C++ - Problema con operador delete
Publicado por: amchacon en 14 Diciembre 2013, 16:58 pm
Vale ya he arreglado el operador de asignación, el problema es que no devuelves una referencia sino un nuevo objeto matriz. Como no tienes hecho un constructor copia se copian los punteros literales, eso te provoca problemas...

Simplemente haz estos dos cambios:

- Haz que el operador = devuelva Matriz& en vez de un nuevo objeto.
- Create un constructor copia tal que así:

Código
  1. Matriz::Matriz(const Matriz &m)
  2. {
  3.    *this = m;
  4. }

Con eso a mí me funciona perfecto.


Título: Re: C++ - Problema con operador delete
Publicado por: xaps en 14 Diciembre 2013, 17:08 pm
Vale ya he arreglado el operador de asignación, el problema es que no devuelves una referencia sino un nuevo objeto matriz. Como no tienes hecho un constructor copia se copian los punteros literales, eso te provoca problemas...

Simplemente haz estos dos cambios:

- Haz que el operador = devuelva Matriz& en vez de un nuevo objeto.
- Create un constructor copia tal que así:

Código
  1. Matriz::Matriz(const Matriz &m)
  2. {
  3.    *this = m;
  4. }

Con eso a mí me funciona perfecto.

¿A que te refieres con "Como no tienes hecho un constructor copia se copian los punteros literales"? No entiendo cual es el problema aún :S


Título: Re: C++ - Problema con operador delete
Publicado por: amchacon en 14 Diciembre 2013, 17:33 pm
¿A que te refieres con "Como no tienes hecho un constructor copia se copian los punteros literales"? No entiendo cual es el problema aún :S
A ver, tu tenías definido el operador = para que devolviese un nuevo objeto matriz.

Como has hecho return *this esa matriz era una copia de this. Para hacer copias de un objeto se llama al constructor copia.

Si no has definido el constructor copia, el compilador crea uno por ti. El problema esque el compilador hace la copia literal. Lo que implica que se copian los punteros de la matriz tal cual.

Cuando esa matriz "copia" desaparece, se llama al destructor y se libera la memoria. Cuando la matriz "original" desaparece, se vuelve a llamar al destructor y se libera la misma memoria (¡Los punteros son iguales!). De ahí la excepción en tiempo de ejcución.

Aunque el error se solucionaría poniendo Matriz& en el retorno del operador =, no puedes dejarlo así porque ya sabes que las copias te van a fallar. Asi que te he rehecho el constructor copia (y para no copy&pastear el código, me he aprovechado del operador de asignación que hemos definido).

PD: Ya que estoy, los demás operadores (+ - *) también debería devolver Matriz& en vez de Matriz. De lo contrario estarás haciendo copias al montón :silbar:


Título: Re: C++ - Problema con operador delete
Publicado por: xaps en 14 Diciembre 2013, 20:01 pm
A ver, tu tenías definido el operador = para que devolviese un nuevo objeto matriz.

Como has hecho return *this esa matriz era una copia de this. Para hacer copias de un objeto se llama al constructor copia.

Si no has definido el constructor copia, el compilador crea uno por ti. El problema esque el compilador hace la copia literal. Lo que implica que se copian los punteros de la matriz tal cual.

Cuando esa matriz "copia" desaparece, se llama al destructor y se libera la memoria. Cuando la matriz "original" desaparece, se vuelve a llamar al destructor y se libera la misma memoria (¡Los punteros son iguales!). De ahí la excepción en tiempo de ejcución.

Aunque el error se solucionaría poniendo Matriz& en el retorno del operador =, no puedes dejarlo así porque ya sabes que las copias te van a fallar. Asi que te he rehecho el constructor copia (y para no copy&pastear el código, me he aprovechado del operador de asignación que hemos definido).

PD: Ya que estoy, los demás operadores (+ - *) también debería devolver Matriz& en vez de Matriz. De lo contrario estarás haciendo copias al montón :silbar:

Vale, el porqué del constructor copia lo he entendido. Pero cuando tu retornas Matriz&, estás retornando una dirección de memoria de una matriz que al terminar la función será eliminada si no me equivoco, además de que el objeto al que se le hace la asignación debería ser un puntero, ¿no?. En cambio, si retornas una copia, asignas la copia a la matriz correspondiente y luego la copia se elimina, ¿cierto?


Título: Re: C++ - Problema con operador delete
Publicado por: amchacon en 14 Diciembre 2013, 20:13 pm
Vale, el porqué del constructor copia lo he entendido. Pero cuando tu retornas Matriz&, estás retornando una dirección de memoria de una matriz que al terminar la función será eliminada si no me equivoco
No, estoy retornando una referencia. En este caso la referencia es this, asi que estoy retornando una referencia al propio objeto.

Eso te permite poner varios = consecutivos, cada uno coge la referencia del anterior ;)

PD: Si no usas la referencia no pasa nada, no es como si estuvieses creando un objeto ni nada parecido.

además de que el objeto al que se le hace la asignación debería ser un puntero, ¿no?. ]En cambio, si retornas una copia, asignas la copia a la matriz correspondiente y luego la copia se elimina, ¿cierto?
¿A que te refieres? No te sigo...


Título: Re: C++ - Problema con operador delete
Publicado por: xaps en 15 Diciembre 2013, 17:17 pm
No, estoy retornando una referencia. En este caso la referencia es this, asi que estoy retornando una referencia al propio objeto.

Eso te permite poner varios = consecutivos, cada uno coge la referencia del anterior ;)

PD: Si no usas la referencia no pasa nada, no es como si estuvieses creando un objeto ni nada parecido.
¿A que te refieres? No te sigo...

Vale, creo que ya lo tengo. Por lo visto, no había entendido bien que era realmente la referencia.

Pero cuando tu retornas Matriz&, estás retornando una dirección de memoria de una matriz que al terminar la función será eliminada si no me equivoco, además de que el objeto al que se le hace la asignación debería ser un puntero, ¿no?.
La parte de que estoy hablando de punteros olvídala, era un fallo de concepto mio.
El resto sería algo como esto:
Citar
Si una función retorna una referencia, ha de tener el mismo cuidado que si la función retornara un puntero. La referencia que se devuelva debe estar ligada a algo que no sea liberado cuando la función retorne. Si no, la referencia se referirá a un trozo de memoria sobre el que ya no tiene control.
http://arco.esi.uclm.es/~david.villa/pensar_en_C++/vol1/ch11s02.html

Por lo que entiendo que en la sobrecarga del operador = sí que se pueda retornar una referencia, ya que trabaja sobre uno de sus parámetros (el implícito). Pero no entiendo porqué debería hacerlo con las sobrecargas de los operadores + - y *, ya que la matriz resultado es una matriz generada dentro de la función, por lo que a la que salga de la función ésta será eliminada y cualquier referencia a ella contendrá datos basura, ¿no?. No se si me explico bien... :S

Gracias por estar resolviéndome estas dudas ^^


Título: Re: C++ - Problema con operador delete
Publicado por: amchacon en 15 Diciembre 2013, 17:55 pm
Vale fallo mío, creía que el operador + era para sumar la misma matriz. Pero por lo que veo genera otra matriz nueva  :silbar:

Para eso te recomiendo que uses una función, un operador se supone que es para hacer una operación en un objeto (no para generar otros).


Título: Re: C++ - Problema con operador delete
Publicado por: xaps en 15 Diciembre 2013, 21:31 pm
Vale fallo mío, creía que el operador + era para sumar la misma matriz. Pero por lo que veo genera otra matriz nueva  :silbar:

Para eso te recomiendo que uses una función, un operador se supone que es para hacer una operación en un objeto (no para generar otros).

Lo hago más que nada para poder realizar operaciones del tipo:
Código:
Matriz m1, m2, m3, m4;
m1 = m2 + m3 + m4;

Es decir, como si fuera un entero o cualquier otro tipo. Pero gracias por la recomendación ^^

Al final he conseguido arreglar la función borrar() a modo de prueba y error, y he averiguado varias cosas:
- Cuando haces delete a un puntero, éste se queda apuntando a la dirección de memoria que lo hacia antes y no pasa a valer NULL.
- La contructora-copiadora que me has pasado tenia un pequeño fallo que me ha hecho pasarme, literalmente, horas a base de prueba y error hasta ver que era lo que estaba ocurriendo (que no te lo estoy echando en cara, todo lo contrario, me ha ayudado a comprender mejor el funcionamiento de POO y te estoy agradecido ^^). Al ser una constructora, había que inicializar el puntero matr a NULL porque si no, cuando hagamos la asignación dentro de la constructora, en la función borrar intentará eliminar unas posiciones de memoria que no son propias del programa, ya que la condición if (this->matr != NULL) la pasaba.

Seguramente me deje alguna cosilla más que quería comentar, si me acuerdo de algo nuevo editaré este comentario ^^ Os adjunto el código de la clase (funciona como mínimo para multiplicar y asignar, falta ver el resto de funciones):

Código
  1. #include <iostream>
  2. #include <vector>
  3.  
  4. using namespace std;
  5.  
  6. class Matriz {
  7.  
  8. private:
  9.  
  10.  int nfilas;
  11.  int ncolumnas;
  12.  int **matr;
  13.  
  14.  void borrar();
  15.  
  16. public:
  17.  
  18.  //Constructoras
  19.  Matriz();
  20.  Matriz(int filas, int columnas);
  21.  
  22.  //Constructora-copiadora
  23.  Matriz(const Matriz& m);
  24.  
  25.  //Destructora
  26.  ~Matriz();
  27.  
  28.  //Consultoras
  29.  int filas() const;
  30.  int columnas() const;
  31.  int consultar(int fila, int columna) const;
  32.  
  33.  //Modificadora
  34.  void modificar(int fila, int columna, int x);
  35.  
  36.  //Entrada / Sortida
  37.  void leer();
  38.  void escribir() const;
  39.  
  40.  //Operadores
  41.  Matriz& operator =(const Matriz &b);
  42.  
  43.  Matriz operator +(const Matriz &b) const;
  44.  Matriz operator -(const Matriz &b) const;
  45.  Matriz operator *(const Matriz &b) const;
  46.  
  47.  bool operator ==(const Matriz &mat) const;
  48.  bool operator !=(const Matriz &mat) const;
  49. };
  50.  
  51. Matriz::Matriz()
  52. {
  53.  nfilas = 0;
  54.  ncolumnas = 0;
  55.  matr = NULL;
  56. }
  57.  
  58. Matriz::Matriz(int filas, int columnas)
  59. {
  60.  this->nfilas = filas;
  61.  this->ncolumnas = columnas;
  62.  
  63.  if (nfilas > 0 and ncolumnas > 0)
  64.  {
  65.    matr = new int* [filas];
  66.    for (int i = 0; i < filas; ++i)
  67.    {
  68.      matr[i] = new int [columnas];
  69.    }
  70.  }
  71.  else
  72.  {
  73.    nfilas = 0;
  74.    ncolumnas = 0;
  75.    matr = NULL;
  76.  }
  77. }
  78.  
  79. Matriz::Matriz(const Matriz &m)
  80. {
  81.  this->matr = NULL;
  82.  *this = m;
  83. }
  84.  
  85. Matriz::~Matriz() { borrar(); }
  86.  
  87. void Matriz::borrar()
  88. {
  89.  if (this->matr != NULL)
  90.  {
  91.    for (int i = 0; i < nfilas; ++i) delete[] matr[i];
  92.    delete[] matr;
  93.    matr = NULL;
  94.  }
  95. }
  96.  
  97. int Matriz::filas() const { return nfilas;}
  98.  
  99. int Matriz::columnas() const { return ncolumnas;}
  100.  
  101. int Matriz::consultar(int fila, int columna) const { return matr[fila][columna];}
  102.  
  103. void Matriz::modificar(int fila, int columna, int x) { matr[fila][columna] = x;}
  104.  
  105. void Matriz::leer()
  106. {
  107.  for (int i = 0; i < nfilas; ++i)
  108.    for (int j = 0; j < ncolumnas; ++j) cin >> matr[i][j];
  109. }
  110.  
  111. void Matriz::escribir() const
  112. {
  113.  for (int i = 0; i < nfilas; ++i)
  114.  {
  115.    for (int j = 0; j < ncolumnas; ++j) cout << matr[i][j] << " ";
  116.    cout << endl;
  117.  }
  118. }
  119.  
  120. Matriz& Matriz::operator =(const Matriz &mat)
  121. {
  122.  if (this != &mat)
  123.  {
  124.    this->borrar();
  125.  
  126.    if (mat.matr)
  127.    {
  128.      this->nfilas = mat.nfilas;
  129.      this->ncolumnas = mat.ncolumnas;
  130.  
  131.      this->matr = new int* [nfilas];
  132.      for (int i = 0; i < nfilas; ++i)
  133. this->matr[i] = new int [ncolumnas];
  134.  
  135.      for (int i = 0; i < nfilas; ++i)
  136.      {
  137. for (int j = 0; j < ncolumnas; ++j)
  138.  this->matr[i][j] = mat.matr[i][j];
  139.      }
  140.    }
  141.  }
  142.  return *this;
  143. }
  144.  
  145. Matriz Matriz::operator +(const Matriz &b) const
  146. {
  147.  int filas = this->nfilas;
  148.  int columnas = this->ncolumnas;
  149.  
  150.  Matriz res(filas, columnas);
  151.  
  152.  for (int i = 0; i < filas; ++i)
  153.    for (int j = 0; j < columnas; ++j)
  154.      res.matr[i][j] = this->matr[i][j] + b.matr[i][j];
  155.  
  156.  return res;
  157. }
  158.  
  159. Matriz Matriz::operator -(const Matriz &b) const
  160. {
  161.  int filas = this->nfilas;
  162.  int columnas = this->ncolumnas;
  163.  
  164.  Matriz res(filas, columnas);
  165.  
  166.  for (int i = 0; i < filas; ++i)
  167.    for (int j = 0; j < columnas; ++j)
  168.      res.matr[i][j] = this->matr[i][j] - b.matr[i][j];
  169.  
  170.  return res;
  171. }
  172.  
  173. Matriz Matriz::operator *(const Matriz &b) const
  174. {
  175.  if (this->ncolumnas == b.nfilas)
  176.  {
  177.    int pos = this->ncolumnas;
  178.    int filas = this->nfilas;
  179.    int columnas = b.ncolumnas;
  180.  
  181.    Matriz res(filas, columnas);
  182.    for (int i = 0; i < filas; ++i)
  183.    {
  184.      for (int j = 0; j < columnas; ++j)
  185.      {
  186. int value = 0;
  187. for (int k = 0; k < pos; ++k) value += this->matr[i][k] * b.matr[k][j];
  188. res.matr[i][j] = value;
  189.      }
  190.    };
  191.    return res;
  192.  }
  193. }
  194.  
  195. bool Matriz::operator ==(const Matriz &mat) const
  196. {
  197.  for (int i = 0; i < nfilas; ++i)
  198.    for (int j = 0; j < ncolumnas; ++j)
  199.      if (this->matr[i][j] != mat.matr[i][j]) return false;
  200.  
  201.  return true;
  202. }
  203.  
  204. bool Matriz::operator !=(const Matriz &mat) const
  205. {
  206.  for (int i = 0; i < nfilas; ++i)
  207.    for (int j = 0; j < ncolumnas; ++j)
  208.      if (this->matr[i][j] != mat.matr[i][j]) return true;
  209.  
  210.  return false;
  211. }
  212.  

Cualquier nueva duda que tenga la haré desde este mismo tema (modificando el título) para no crear nuevos temas innecesarios y no se repita la información.
Si veis algún cambio raro en el código que no haya comentado y que no se entienda comentarlo y os lo explicaré encantado.

Saludos y muchas gracias amchacon.


Título: Re: C++ - Problema con operador delete
Publicado por: amchacon en 15 Diciembre 2013, 22:08 pm
Lo hago más que nada para poder realizar operaciones del tipo:
Código:
Matriz m1, m2, m3, m4;
m1 = m2 + m3 + m4;

Es decir, como si fuera un entero o cualquier otro tipo. Pero gracias por la recomendación ^^
Esa sentencia funcionaría exactamente igual con referencias, además sería muchísimo más eficiente (no tienes que estar copiando, creando nuevos objetos, borrando...).

De hecho yo me definiría y usaría el operador +=, que eso siempre va a ser más eficiente que hacer dos por separado (+ y =).

A- La contructora-copiadora que me has pasado tenia un pequeño fallo que me ha hecho pasarme, literalmente, horas a base de prueba y error hasta ver que era lo que estaba ocurriendo (que no te lo estoy echando en cara, todo lo contrario, me ha ayudado a comprender mejor el funcionamiento de POO y te estoy agradecido ^^). Al ser una constructora, había que inicializar el puntero matr a NULL porque si no, cuando hagamos la asignación dentro de la constructora, en la función borrar intentará eliminar unas posiciones de memoria que no son propias del programa, ya que la condición if (this->matr != NULL) la pasaba.
Sí bueno, yo tengo la mala suerte de atraer los errores estúpidos. Codeo demasiado rápido a veces :silbar:

En C++11 puedes inicializar una variable por defecto en una clase. De formas que no tienes que estar matr = NULL en todos los constructores xD


Título: Re: C++ - Problema con operador delete
Publicado por: xaps en 16 Diciembre 2013, 01:49 am
Esa sentencia funcionaría exactamente igual con referencias, además sería muchísimo más eficiente (no tienes que estar copiando, creando nuevos objetos, borrando...).
Pero si lo hiciera por referencias estaría modificado las matrices que estoy sumando, ¿no?

De hecho yo me definiría y usaría el operador +=, que eso siempre va a ser más eficiente que hacer dos por separado (+ y =).
La verdad, no se me había ocurrido, pero me parece buena idea añadirlo, además de que tengo pendiente sobrecargar los operadores << y >> de iostream para la entrada/salida de datos. También pensé en los operadores ++ y --, pero no les veo utilidad para matrices.

En C++11 puedes inicializar una variable por defecto en una clase. De formas que no tienes que estar matr = NULL en todos los constructores xD
Voy a investigar sobre esto, parece bastante interesante. Muchas gracias ^^

EDITO: He modificado la constructora añadiendo los valores por defecto y gracias a esto he conseguido ahorrarme la constructora que tenía sin parámetros ^^ Aquí el código:

Código
  1. Matriz::Matriz(int filas = 0, int columnas = 0) : nfilas(filas), ncolumnas(columnas)
  2. {
  3.  if (nfilas > 0 and ncolumnas > 0)
  4.  {
  5.    matr = new int* [filas];
  6.    for (int i = 0; i < filas; ++i)
  7.    {
  8.      matr[i] = new int [columnas];
  9.    }
  10.  }
  11.  else matr = NULL;
  12. }
  13.  

También he modificado la constructora para que modifique los parámetros de la clase antes del propio código. Según he leído, esto sirve para inicializar objetos de otras clases usando sus constructores y no los por defecto, pero ésto no se podía hacer de otra manera también? Es decir, ¿estos dos códigos no harían lo mismo?

Código
  1. Clase1::Clase1(Clase2 obj) : parametro1(clase2) {}
Código
  1. Clase1::Clase1(Clase2 obj)
  2. {
  3.  parametro1 = Clase1 (obj);
  4. }

Gracias! ^^


Título: Re: C++ - Problema con operador delete
Publicado por: amchacon en 16 Diciembre 2013, 09:32 am
Pero si lo hiciera por referencias estaría modificado las matrices que estoy sumando, ¿no?
Es verdad, no había caído en eso.

Aunque solo modificarías la primera.

Voy a investigar sobre esto, parece bastante interesante. Muchas gracias ^^

EDITO: He modificado la constructora añadiendo los valores por defecto y gracias a esto he conseguido ahorrarme la constructora que tenía sin parámetros ^^ Aquí el código:

Código
  1. Matriz::Matriz(int filas = 0, int columnas = 0) : nfilas(filas), ncolumnas(columnas)
  2. {
  3.  if (nfilas > 0 and ncolumnas > 0)
  4.  {
  5.    matr = new int* [filas];
  6.    for (int i = 0; i < filas; ++i)
  7.    {
  8.      matr[i] = new int [columnas];
  9.    }
  10.  }
  11.  else matr = NULL;
  12. }
  13.  

También he modificado la constructora para que modifique los parámetros de la clase antes del propio código. Según he leído, esto sirve para inicializar objetos de otras clases usando sus constructores y no los por defecto, pero ésto no se podía hacer de otra manera también? Es decir, ¿estos dos códigos no harían lo mismo?

Código
  1. Clase1::Clase1(Clase2 obj) : parametro1(clase2) {}
Código
  1. Clase1::Clase1(Clase2 obj)
  2. {
  3.  parametro1 = Clase1 (obj);
  4. }

Gracias! ^^
En realidad no me refería a eso (para eso no hace falta recurrir a C++11). Yo me refería a:

Código
  1. class Matriz
  2. {
  3.  int ** matr = NULL;
  4.  //...
  5. }

Como ves, lo que inicializo es el mismo atributo de la clase. Así no tengo que ponerlo en todos los constructores.


Título: Re: C++ - Problema con operador delete
Publicado por: xaps en 16 Diciembre 2013, 15:41 pm
Es verdad, no había caído en eso.

Aunque solo modificarías la primera.
En realidad no me refería a eso (para eso no hace falta recurrir a C++11). Yo me refería a:

Código
  1. class Matriz
  2. {
  3.  int ** matr = NULL;
  4.  //...
  5. }

Como ves, lo que inicializo es el mismo atributo de la clase. Así no tengo que ponerlo en todos los constructores.

Vaya, creía que te referías a lo otro que había añadido... Creo que he querido ir demasiado rápido al implementarlo y he lo he hecho con lo primero que he encontrado  :silbar: Aunque tampoco me ha ido mal ^^

Pero bueno, me ha parecido buena idea también y lo he incorporado a la clase, aunque solo lo he hecho con el parámetro matr, ya que he considerado mejor que los otros dos parámetros los deje por defecto en el constructor y que se asignen a la vez que se crea una nueva instancia del objeto.

Esta vez he hecho bastantes más cambios. He añadido los operadores +=, -=, >>(istream) y << (ostream), por lo que he eliminado las funciones leer() y escribir().  También he añadido el producto de una matriz por un escalar y he intentado simular la propiedad de conmutatividad, aunque no se si lo he hecho de la forma correcta. También he cambiado la mayoría de sobrecargas y las he hecho amigas de la clase, de manera que sean externas y no usen el parámetro implícito, ya que me parecía un poco feo utilizar solo el parámetro implícito como consulta pudiendo poner las funciones como amigas de la clase. También he arreglado un par de cosas que podían hacer petar el programa y he probado todas las funciones un poco por encima.

Aquí el código de la clase y del programa que estoy usando para hacer las pruebas:

Matriz.h:
Código
  1. #include <iostream>
  2.  
  3. using namespace std;
  4.  
  5. class Matriz {
  6.  
  7. private:
  8.  
  9.  int nfilas;
  10.  int ncolumnas;
  11.  int **matr = NULL;
  12.  
  13.  void borrar();
  14.  
  15. public:
  16.  
  17.  //Constructora
  18.  Matriz(int filas, int columnas);
  19.  
  20.  //Constructora-copiadora
  21.  Matriz(const Matriz& m);
  22.  
  23.  //Destructora
  24.  ~Matriz();
  25.  
  26.  //Consultoras
  27.  int filas() const;
  28.  int columnas() const;
  29.  int consultar(int fila, int columna) const;
  30.  
  31.  //Modificadora
  32.  void modificar(int fila, int columna, int x);
  33.  
  34.  //Operadores
  35.  Matriz& operator =(const Matriz &b);
  36.  
  37.  void operator +=(const Matriz &mat);
  38.  void operator -=(const Matriz &mat);
  39.  
  40.  friend Matriz operator +(const Matriz &a, const Matriz &b);
  41.  friend Matriz operator -(const Matriz &a, const Matriz &b);
  42.  friend Matriz operator *(const Matriz &a, const Matriz &b);
  43.  friend Matriz operator *(const Matriz &a, int b);
  44.  friend Matriz operator *(int a, const Matriz &b);
  45.  
  46.  friend bool operator ==(const Matriz &a, const Matriz &b);
  47.  friend bool operator !=(const Matriz &a, const Matriz &b);
  48.  
  49.  friend ostream& operator <<(ostream &salida, const Matriz &mat);
  50.  friend istream& operator >>(istream &entrada, Matriz &mat);
  51. };
  52.  
  53. Matriz::Matriz(int filas = 0, int columnas = 0) : nfilas(filas), ncolumnas(columnas)
  54. {
  55.  if (nfilas > 0 and ncolumnas > 0)
  56.  {
  57.    matr = new int* [filas];
  58.    for (int i = 0; i < filas; ++i) matr[i] = new int [columnas];
  59.  }
  60. }
  61.  
  62. Matriz::Matriz(const Matriz &m) { *this = m; }
  63.  
  64. Matriz::~Matriz() { borrar(); }
  65.  
  66. void Matriz::borrar()
  67. {
  68.  if (this->matr != NULL)
  69.  {
  70.    for (int i = 0; i < nfilas; ++i) delete[] matr[i];
  71.    delete[] matr;
  72.    matr = NULL;
  73.  }
  74. }
  75.  
  76. int Matriz::filas() const { return nfilas;}
  77.  
  78. int Matriz::columnas() const { return ncolumnas;}
  79.  
  80. int Matriz::consultar(int fila, int columna) const { return matr[fila][columna];}
  81.  
  82. void Matriz::modificar(int fila, int columna, int x) { matr[fila][columna] = x;}
  83.  
  84. Matriz& Matriz::operator =(const Matriz &mat)
  85. {
  86.  if (this != &mat)
  87.  {
  88.    this->borrar();
  89.  
  90.    if (mat.matr)
  91.    {
  92.      this->nfilas = mat.nfilas;
  93.      this->ncolumnas = mat.ncolumnas;
  94.  
  95.      this->matr = new int* [nfilas];
  96.      for (int i = 0; i < nfilas; ++i)
  97. this->matr[i] = new int [ncolumnas];
  98.  
  99.      for (int i = 0; i < nfilas; ++i)
  100.      {
  101. for (int j = 0; j < ncolumnas; ++j)
  102.  this->matr[i][j] = mat.matr[i][j];
  103.      }
  104.    }
  105.  }
  106.  return *this;
  107. }
  108.  
  109. void Matriz::operator +=(const Matriz &mat)
  110. {
  111.  if (this->nfilas != 0 and this->ncolumnas != 0 and mat.nfilas != 0 and mat.ncolumnas != 0)
  112.  {
  113.    for (int i = 0; i < nfilas; ++i)
  114.      for (int j = 0; j < ncolumnas; ++j)
  115. this->matr[i][j] += mat.matr[i][j];
  116.  }
  117. }
  118.  
  119. void Matriz::operator -=(const Matriz &mat)
  120. {
  121.  if (this->nfilas != 0 and this->ncolumnas != 0 and mat.nfilas != 0 and mat.ncolumnas != 0)
  122.  {
  123.    for (int i = 0; i < nfilas; ++i)
  124.      for (int j = 0; j < ncolumnas; ++j)
  125. this->matr[i][j] -= mat.matr[i][j];
  126.  }
  127. }
  128.  
  129. Matriz operator +(const Matriz &a, const Matriz &b)
  130. {
  131.  int filas = a.nfilas;
  132.  int columnas = a.ncolumnas;
  133.  
  134.  Matriz res(filas, columnas);
  135.  
  136.  for (int i = 0; i < filas; ++i)
  137.    for (int j = 0; j < columnas; ++j)
  138.      res.matr[i][j] = a.matr[i][j] + b.matr[i][j];
  139.  
  140.  return res;
  141. }
  142.  
  143. Matriz operator -(const Matriz &a, const Matriz &b)
  144. {
  145.  int filas = a.nfilas;
  146.  int columnas = a.ncolumnas;
  147.  
  148.  Matriz res(filas, columnas);
  149.  
  150.  for (int i = 0; i < filas; ++i)
  151.    for (int j = 0; j < columnas; ++j)
  152.      res.matr[i][j] = a.matr[i][j] - b.matr[i][j];
  153.  
  154.  return res;
  155. }
  156.  
  157. Matriz operator *(const Matriz &a, const Matriz &b)
  158. {
  159.  if (a.ncolumnas == b.nfilas)
  160.  {
  161.    int pos = a.ncolumnas;
  162.  
  163.    int filas = a.nfilas;
  164.    int columnas = b.ncolumnas;
  165.  
  166.    Matriz res(filas, columnas);
  167.    for (int i = 0; i < filas; ++i)
  168.    {
  169.      for (int j = 0; j < columnas; ++j)
  170.      {
  171. int value = 0;
  172. for (int k = 0; k < pos; ++k) value += a.matr[i][k] * b.matr[k][j];
  173. res.matr[i][j] = value;
  174.      }
  175.    }
  176.    return res;
  177.  }
  178. }
  179.  
  180. Matriz operator *(const Matriz &a, int b)
  181. {
  182.  int filas = a.nfilas;
  183.  int columnas = a.ncolumnas;
  184.  
  185.  Matriz res(filas, columnas);
  186.  for (int i = 0; i < filas; ++i)
  187.    for (int j = 0; j < columnas; ++j) res.matr[i][j] = a.matr[i][j] * b;
  188.  
  189.  return res;
  190. }
  191.  
  192. Matriz operator *(int a, const Matriz &b) { return operator*(b, a); }
  193.  
  194. bool operator ==(const Matriz &a, const Matriz &b)
  195. {
  196.  if (a.nfilas != b.nfilas or a.ncolumnas != b.ncolumnas) return false;
  197.  for (int i = 0; i < a.nfilas; ++i)
  198.    for (int j = 0; j < a.ncolumnas; ++j)
  199.      if (a.matr[i][j] != b.matr[i][j]) return false;
  200.  
  201.  return true;
  202. }
  203.  
  204. bool operator !=(const Matriz &a, const Matriz &b)
  205. {
  206.  if (a.nfilas != b.nfilas or a.ncolumnas != b.ncolumnas) return true;
  207.  for (int i = 0; i < a.nfilas; ++i)
  208.    for (int j = 0; j < a.ncolumnas; ++j)
  209.      if (a.matr[i][j] != b.matr[i][j]) return true;
  210.  
  211.  return false;
  212. }
  213.  
  214. ostream& operator <<(ostream &salida, const Matriz &mat)
  215. {
  216.  for (int i = 0; i < mat.nfilas; ++i)
  217.  {
  218.    for (int j = 0; j < mat.ncolumnas; ++j) salida << mat.matr[i][j] << " ";
  219.    salida << endl;
  220.  }
  221.  return salida;
  222. }
  223.  
  224. istream& operator >>(istream &entrada, Matriz &mat)
  225. {
  226.  for (int i = 0; i < mat.nfilas; ++i)
  227.    for (int j = 0; j < mat.ncolumnas; ++j) entrada >> mat.matr[i][j];
  228.  return entrada;
  229. }

test.cpp:
Código
  1. #include "Matriz.h"
  2.  
  3. int main()
  4. {
  5.  int f1, c1, f2, c2;
  6.  
  7.  cout << "Introduce el tamaño de la primera matriz:" << endl;
  8.  cin >> f1 >> c1;
  9.  Matriz mat1(f1, c1);
  10.  cout << "Introduce los valores de la matriz:" << endl;
  11.  cin >> mat1;
  12.  
  13.  cout << "Introduce el tamaño de la segunda matriz:" << endl;
  14.  cin >> f2 >> c2;
  15.  Matriz mat2(f2, c2);
  16.  cout << "Introduce los valores de la matriz:" << endl;
  17.  cin >> mat2;
  18.  
  19.  
  20.  if (c1 == f2)
  21.  {
  22.    Matriz res;
  23.    res = mat1 * mat2;
  24.    cout << res << endl;
  25.  
  26.    res = mat1 + mat2;
  27.    cout << res << endl;
  28.  
  29.    res = mat1 - mat2;
  30.    cout << res << endl;
  31.  
  32.    res = res * 4;
  33.    cout << res << endl;
  34.  
  35.    res = 2 * res;
  36.    cout << res << endl;
  37.  
  38.    res += mat1;
  39.    cout << res << endl;
  40.  
  41.    res -= mat2;
  42.    cout << res << endl;
  43.  
  44.    if (res == mat1) cout << "Igualdad incorrecta" << endl;
  45.    else cout << "Igualdad correcta" << endl;
  46.    if (res != mat1) cout << "Desigualdad correcta" << endl;
  47.    else cout << "Desigualdad incorrecta" << endl;
  48.  }
  49. }

¿Que te va pareciendo el código? ¿Crees que hay algo que mejorar o que añadirle? Tengo pensado añadir Gauss a la clase para escalonar y resolver sistemas, pero antes quiero tener la parte más básica bien hecha para luego no tener tantos problemas ^^

Gracias por la ayuda!


Título: Re: C++ - Problema con operador delete
Publicado por: amchacon en 16 Diciembre 2013, 15:54 pm
Código
  1. bool operator ==(const Matriz &a, const Matriz &b)
  2. {
  3. if (a.nfilas != b.nfilas or a.ncolumnas != b.ncolumnas) return false;
  4. for (int i = 0; i < a.nfilas; ++i)
  5.   for (int j = 0; j < a.ncolumnas; ++j)
  6.     if (a.matr[i][j] != b.matr[i][j]) return false;
  7.  
  8. return true;
  9. }
  10.  
  11. bool operator !=(const Matriz &a, const Matriz &b)
  12. {
  13. if (a.nfilas != b.nfilas or a.ncolumnas != b.ncolumnas) return true;
  14. for (int i = 0; i < a.nfilas; ++i)
  15.   for (int j = 0; j < a.ncolumnas; ++j)
  16.     if (a.matr[i][j] != b.matr[i][j]) return true;
  17.  
  18. return false;
  19. }

Esas líneas se pueden reducir a:

Código
  1. bool operator ==(const Matriz &a, const Matriz &b)
  2. {
  3. if (a.nfilas != b.nfilas or a.ncolumnas != b.ncolumnas) return false;
  4. for (int i = 0; i < a.nfilas; ++i)
  5.   for (int j = 0; j < a.ncolumnas; ++j)
  6.     if (a.matr[i][j] != b.matr[i][j]) return false;
  7.  
  8. return true;
  9. }
  10.  
  11. bool operator !=(const Matriz &a, const Matriz &b)
  12. {
  13. return !(a == b);
  14. }

Te falta por añadir los determinantes (para una matriz nxn). Y los operadores *= y /= (este ultimo con números enteros).


Título: Re: C++ - Problema con operador delete
Publicado por: xaps en 16 Diciembre 2013, 16:56 pm
Código
  1. bool operator ==(const Matriz &a, const Matriz &b)
  2. {
  3. if (a.nfilas != b.nfilas or a.ncolumnas != b.ncolumnas) return false;
  4. for (int i = 0; i < a.nfilas; ++i)
  5.   for (int j = 0; j < a.ncolumnas; ++j)
  6.     if (a.matr[i][j] != b.matr[i][j]) return false;
  7.  
  8. return true;
  9. }
  10.  
  11. bool operator !=(const Matriz &a, const Matriz &b)
  12. {
  13. if (a.nfilas != b.nfilas or a.ncolumnas != b.ncolumnas) return true;
  14. for (int i = 0; i < a.nfilas; ++i)
  15.   for (int j = 0; j < a.ncolumnas; ++j)
  16.     if (a.matr[i][j] != b.matr[i][j]) return true;
  17.  
  18. return false;
  19. }

Esas líneas se pueden reducir a:

Código
  1. bool operator ==(const Matriz &a, const Matriz &b)
  2. {
  3. if (a.nfilas != b.nfilas or a.ncolumnas != b.ncolumnas) return false;
  4. for (int i = 0; i < a.nfilas; ++i)
  5.   for (int j = 0; j < a.ncolumnas; ++j)
  6.     if (a.matr[i][j] != b.matr[i][j]) return false;
  7.  
  8. return true;
  9. }
  10.  
  11. bool operator !=(const Matriz &a, const Matriz &b)
  12. {
  13. return !(a == b);
  14. }
Cierto. Aún no estoy muy acostumbrado a reciclar código...  :silbar:

Te falta por añadir los determinantes (para una matriz nxn).
Para cálculo de determinantes el mejor método es Laplace, ¿verdad?

Y los operadores *= y /= (este ultimo con números enteros).
Cierto. Aunque la división no la he implementado por dos razones:
- La primera, nunca he dividido dos matrices. Supongo que no debe usarse demasiado o que no lo he necesitado de momento.
- La segunda, el hecho de que esté trabajando con enteros hace que la mayoría de divisiones sean inválidas.

Debería cambiar el tipo numérico de la matriz a float para poder realizar divisiones, ¿no? Ya que para escalonar y hacer Gauss probablemente necesite dividir según que posiciones de la matriz.


Título: Re: C++ - Problema con operador delete
Publicado por: amchacon en 16 Diciembre 2013, 17:12 pm
Para cálculo de determinantes el mejor método es Laplace, ¿verdad?
Es el que uso yo, aunque yo detengo el algoritmo cuando es 2x2.

Cierto. Aunque la división no la he implementado por dos razones:
- La primera, nunca he dividido dos matrices. Supongo que no debe usarse demasiado o que no lo he necesitado de momento.
Dividir 2 matrices es multiplicar por su inversa.

Pero no me refería a eso sino a la división con un entero.

La segunda, el hecho de que esté trabajando con enteros hace que la mayoría de divisiones sean inválidas.
Quizás deberías plantearte hacerlo todo a double (o mejor aún, hazlo en formato plantilla y que el usuario decida).


Título: Re: C++ - Problema con operador delete
Publicado por: xaps en 16 Diciembre 2013, 17:29 pm
Dividir 2 matrices es multiplicar por su inversa.
Cierto, no se en que estaría pensando.

Quizás deberías plantearte hacerlo todo a double (o mejor aún, hazlo en formato plantilla y que el usuario decida).
Estaba pensando en usar los templates, pero entonces tengo el problema de que a alguien se le ocurra declarar una matriz de un tipo que no sea numérico, como char o alguna clase no genérica. ¿Que se puede hacer en estos casos? He visto que se pueden hacer especializaciones, pero es imposible hacer una especialización para cada tipo de dato que considere no válido para mi matriz :S


Título: Re: C++ - Problema con operador delete
Publicado por: amchacon en 16 Diciembre 2013, 18:07 pm
Estaba pensando en usar los templates, pero entonces tengo el problema de que a alguien se le ocurra declarar una matriz de un tipo que no sea numérico, como char
char es un dato número, es como si fuera un int de 1 byte.

o alguna clase no genérica. ¿Que se puede hacer en estos casos?
No hay ningún problema mientras la clase que use tenga definido los operadores correspondientes (+ - * / ==...).

O al menos tenga definido los operadores de las funciones que vaya a usar.


Título: Re: C++ - Problema con operador delete
Publicado por: xaps en 17 Diciembre 2013, 02:54 am
char es un dato número, es como si fuera un int de 1 byte.
No hay ningún problema mientras la clase que use tenga definido los operadores correspondientes (+ - * / ==...).

O al menos tenga definido los operadores de las funciones que vaya a usar.
Pues me he puesto a estudiar y implementar los templates en la clase, pero me he encontrado con un problema que no me deja avanzar. La mayoría de mis sobrecargas de operadores son funciones amigas de la función, y por lo visto en general hay problemas al implementar los templates con funciones amigas. No sé si por suerte o por desgracia, la única función que parece no encajar bien con los templates es la de sobrecarga del producto. A ver si me podéis ayudar con esto, que llevo un buen rato peleándome con el error buscando, moviendo y cambiando código... Pero no lo consigo.

Vuelvo a pegar el código entero de la clase (ya que se han tenido que cambiar bastantes cosas) y el mensaje de error que estoy recibiendo:

Matriz.h:
Código
  1. #include <iostream>
  2.  
  3. using namespace std;
  4.  
  5. template <class T>
  6. class Matriz {
  7.  
  8. private:
  9.  
  10.  int nfilas;
  11.  int ncolumnas;
  12.  T **matr = NULL;
  13.  
  14.  void borrar();
  15.  
  16. public:
  17.  
  18.  //Constructora
  19.  Matriz(int filas, int columnas);
  20.  
  21.  //Constructora-copiadora
  22.  Matriz(const Matriz<T> &m);
  23.  
  24.  //Destructora
  25.  ~Matriz();
  26.  
  27.  //Consultoras
  28.  int filas() const;
  29.  int columnas() const;
  30.  T consultar(int fila, int columna) const;
  31.  
  32.  //Modificadora
  33.  void modificar(int fila, int columna, T x);
  34.  
  35.  //Operadores
  36.  Matriz& operator =(const Matriz<T> &b);
  37.  
  38.  void operator +=(const Matriz<T> &mat);
  39.  void operator -=(const Matriz<T> &mat);
  40.  
  41.  friend Matriz operator + <>(const Matriz<T> &a, const Matriz<T> &b);
  42.  friend Matriz operator - <>(const Matriz<T> &a, const Matriz<T> &b);
  43.  friend Matriz operator * <>(const Matriz<T> &a, const Matriz<T> &b);
  44.  friend Matriz operator * <>(const Matriz<T> &a, int b);
  45.  friend Matriz operator * <>(int a, const Matriz<T> &b);
  46.  
  47.  friend bool operator == <>(const Matriz<T> &a, const Matriz<T> &b);
  48.  friend bool operator != <>(const Matriz<T> &a, const Matriz<T> &b);
  49.  
  50.  friend ostream& operator << <>(ostream &salida, const Matriz<T> &mat);
  51.  friend istream& operator >> <>(istream &entrada, Matriz<T> &mat);
  52. };
  53.  
  54. template <class T>
  55. Matriz<T>::Matriz(int filas = 0, int columnas = 0) : nfilas(filas), ncolumnas(columnas)
  56. {
  57.  if (nfilas > 0 and ncolumnas > 0)
  58.  {
  59.    matr = new T* [filas];
  60.    for (int i = 0; i < filas; ++i) matr[i] = new T [columnas];
  61.  }
  62. }
  63.  
  64. template <class T>
  65. Matriz<T>::Matriz(const Matriz<T> &m) { *this = m; }
  66.  
  67. template <class T>
  68. Matriz<T>::~Matriz() { borrar(); }
  69.  
  70. template <class T>
  71. void Matriz<T>::borrar()
  72. {
  73.  if (this->matr != NULL)
  74.  {
  75.    for (int i = 0; i < nfilas; ++i) delete[] matr[i];
  76.    delete[] matr;
  77.    matr = NULL;
  78.  }
  79. }
  80.  
  81. template <class T>
  82. int Matriz<T>::filas() const { return nfilas;}
  83.  
  84. template <class T>
  85. int Matriz<T>::columnas() const { return ncolumnas;}
  86.  
  87. template <class T>
  88. T Matriz<T>::consultar(int fila, int columna) const { return matr[fila][columna];}
  89.  
  90. template <class T>
  91. void Matriz<T>::modificar(int fila, int columna, T x) { matr[fila][columna] = x;}
  92.  
  93. template <class T>
  94. Matriz<T>& Matriz<T>::operator =(const Matriz<T> &mat)
  95. {
  96.  if (this != &mat)
  97.  {
  98.    this->borrar();
  99.  
  100.    if (mat.matr)
  101.    {
  102.      this->nfilas = mat.nfilas;
  103.      this->ncolumnas = mat.ncolumnas;
  104.  
  105.      this->matr = new int* [nfilas];
  106.      for (int i = 0; i < nfilas; ++i)
  107. this->matr[i] = new int [ncolumnas];
  108.  
  109.      for (int i = 0; i < nfilas; ++i)
  110.      {
  111. for (int j = 0; j < ncolumnas; ++j)
  112.  this->matr[i][j] = mat.matr[i][j];
  113.      }
  114.    }
  115.  }
  116.  return *this;
  117. }
  118.  
  119. template <class T>
  120. void Matriz<T>::operator +=(const Matriz<T> &mat)
  121. {
  122.  if (this->nfilas != 0 and this->ncolumnas != 0 and mat.nfilas != 0 and mat.ncolumnas != 0)
  123.  {
  124.    for (int i = 0; i < nfilas; ++i)
  125.      for (int j = 0; j < ncolumnas; ++j)
  126. this->matr[i][j] += mat.matr[i][j];
  127.  }
  128. }
  129.  
  130. template <class T>
  131. void Matriz<T>::operator -=(const Matriz<T> &mat)
  132. {
  133.  if (this->nfilas != 0 and this->ncolumnas != 0 and mat.nfilas != 0 and mat.ncolumnas != 0)
  134.  {
  135.    for (int i = 0; i < nfilas; ++i)
  136.      for (int j = 0; j < ncolumnas; ++j)
  137. this->matr[i][j] -= mat.matr[i][j];
  138.  }
  139. }
  140.  
  141. template <class T>
  142. Matriz<T> operator +(const Matriz<T> &a, const Matriz<T> &b)
  143. {
  144.  int filas = a.nfilas;
  145.  int columnas = a.ncolumnas;
  146.  
  147.  Matriz<T> res(filas, columnas);
  148.  
  149.  for (int i = 0; i < filas; ++i)
  150.    for (int j = 0; j < columnas; ++j)
  151.      res.matr[i][j] = a.matr[i][j] + b.matr[i][j];
  152.  
  153.  return res;
  154. }
  155.  
  156. template <class T>
  157. Matriz<T> operator -(const Matriz<T> &a, const Matriz<T> &b)
  158. {
  159.  int filas = a.nfilas;
  160.  int columnas = a.ncolumnas;
  161.  
  162.  Matriz<T> res(filas, columnas);
  163.  
  164.  for (int i = 0; i < filas; ++i)
  165.    for (int j = 0; j < columnas; ++j)
  166.      res.matr[i][j] = a.matr[i][j] - b.matr[i][j];
  167.  
  168.  return res;
  169. }
  170.  
  171. template <class T>
  172. Matriz<T> operator *(const Matriz<T> &a, const Matriz<T> &b)
  173. {
  174.  if (a.ncolumnas == b.nfilas)
  175.  {
  176.    int pos = a.ncolumnas;
  177.  
  178.    int filas = a.nfilas;
  179.    int columnas = b.ncolumnas;
  180.  
  181.    Matriz<T> res(filas, columnas);
  182.    for (int i = 0; i < filas; ++i)
  183.    {
  184.      for (int j = 0; j < columnas; ++j)
  185.      {
  186. int value = 0;
  187. for (int k = 0; k < pos; ++k) value += a.matr[i][k] * b.matr[k][j];
  188. res.matr[i][j] = value;
  189.      }
  190.    }
  191.    return res;
  192.  }
  193. }
  194.  
  195. template <class T>
  196. Matriz<T> operator *(const Matriz<T> &a, int b)
  197. {
  198.  int filas = a.nfilas;
  199.  int columnas = a.ncolumnas;
  200.  
  201.  Matriz<T> res(filas, columnas);
  202.  for (int i = 0; i < filas; ++i)
  203.    for (int j = 0; j < columnas; ++j) res.matr[i][j] = a.matr[i][j] * b;
  204.  
  205.  return res;
  206. }
  207.  
  208. template <class T>
  209. Matriz<T> operator *(int a, const Matriz<T> &b) { return b*a; }
  210.  
  211. template <class T>
  212. bool operator ==(const Matriz<T> &a, const Matriz<T> &b)
  213. {
  214.  if (a.nfilas != b.nfilas or a.ncolumnas != b.ncolumnas) return false;
  215.  for (int i = 0; i < a.nfilas; ++i)
  216.    for (int j = 0; j < a.ncolumnas; ++j)
  217.      if (a.matr[i][j] != b.matr[i][j]) return false;
  218.  
  219.  return true;
  220. }
  221.  
  222. template <class T>
  223. bool operator !=(const Matriz<T> &a, const Matriz<T> &b) { return !(a == b); }
  224.  
  225. template <class T>
  226. ostream& operator <<(ostream &salida, const Matriz<T> &mat)
  227. {
  228.  for (int i = 0; i < mat.nfilas; ++i)
  229.  {
  230.    for (int j = 0; j < mat.ncolumnas; ++j) salida << mat.matr[i][j] << " ";
  231.    salida << endl;
  232.  }
  233.  return salida;
  234. }
  235.  
  236. template <class T>
  237. istream& operator >>(istream &entrada, Matriz<T> &mat)
  238. {
  239.  for (int i = 0; i < mat.nfilas; ++i)
  240.    for (int j = 0; j < mat.ncolumnas; ++j) entrada >> mat.matr[i][j];
  241.  return entrada;
  242. }

Y el error que recibo al compilar la clase es el siguiente:
Código:
Matriz.h:43:26: error: declaration of ‘operator*’ as non-function
Matriz.h:43:26: error: expected ‘;’ at end of member declaration
Matriz.h:43:28: error: expected unqualified-id before ‘<’ token
Matriz.h:44:26: error: declaration of ‘operator*’ as non-function
Matriz.h:44:26: error: expected ‘;’ at end of member declaration
Matriz.h:44:28: error: expected unqualified-id before ‘<’ token
Matriz.h:45:26: error: declaration of ‘operator*’ as non-function
Matriz.h:45:26: error: expected ‘;’ at end of member declaration
Matriz.h:45:28: error: expected unqualified-id before ‘<’ token

Como podéis ver he añadido "<>" al final del nombre de cada función amiga de la clase, ya que buscando solución a un problema anterior he visto que la mayoría de códigos implementaban esa metodología para las funciones amigas de la clase. Hay más información sobre ésto aquí: http://c.conclase.net/curso/?cap=040c#042_amigaplantilla
Tampoco descarto que os encontréis alguna que otra barbaridad con los templates :silbar:

Gracias por la ayuda! ^^


Título: Re: C++ - Problema con operador delete
Publicado por: amchacon en 17 Diciembre 2013, 19:37 pm
La implementación no coindice con la declaración en la clase (Matriz != Matriz<T>).


Título: Re: C++ - Problema con operador delete
Publicado por: xaps en 17 Diciembre 2013, 22:11 pm
La implementación no coindice con la declaración en la clase (Matriz != Matriz<T>).
No se a que te refieres exactamente... ¿Dices que en la declaración en la clase de las funciones amigas, he puesto Matriz en vez de Matriz<T>? Aunque lo cambie sigo teniendo el mismo problema :S
Creo que el compilador está dejando de mostrarme errores a partir de que encuentra ese, porque ayer ya estuve corrigiendo varias malas implementaciones del template y el compilador no se quejaba.  :silbar:

De todas formas, hoy le he preguntado a mi profesor de programación de la universidad sobre esto, y me ha dicho que si podía implementarlo como miembro de la clase y aplicarle la etiqueta const hasta ganaría eficiencia, ya que el compilador ya trabajaría de manera distinta que si no fuera una función constante. También me ha dicho que no desarrolla una clase y implementa sobrecargas de operadores y funciones amigas desde hace varios años, por lo que los métodos pueden haber cambiado. ¿Que opináis sobre esto?


Título: Re: C++ - Problema con operador delete
Publicado por: amchacon en 18 Diciembre 2013, 22:11 pm
Yo lo de operadores "amigos" nunca los he usado, asi que no te puedo ayudar más en eso  :silbar:

Los de los const es bastante importante ahora que lo dices, no solo con los operadores sino con las funciones también.


Título: Re: C++ - Problema con operador delete
Publicado por: xaps en 19 Diciembre 2013, 14:15 pm
Pues creo que voy a hacer lo que me ha dicho mi profesor, pondré las sobrecargas como miembros de la clase y aprovecharé para declararlas constantes.

En cuanto tenga una versión presentable la subo para que la veáis ^^

Gracias por la ayuda!

EDITO:

Ya he conseguido implementar la clase con templates. Os añado el código y a continuación os explico un poco que he hecho/modificado para que me funcione.

Matriz.h
Código
  1. #include <iostream>
  2.  
  3. using namespace std;
  4.  
  5. /*ostream y iostream no son templates, no entienden los cast dinámicos que se hacen con un template,
  6.  *es por esto que se necesita redefinir la clase y el método antes que el template, para convertirlo en template
  7.  *http://www.elrincondelc.com/nuevorincon/foros/viewtopic.php?t=13415&view=next&sid=02c39e45352bd867b56de65ebb1afd22
  8.  */
  9. template <class T> class Matriz;
  10. template <class T> ostream& operator <<(ostream &salida, const Matriz<T> &mat);
  11. template <class T> istream& operator >>(istream &entrada, Matriz<T> &mat);
  12. /* --- */
  13.  
  14. template <class T> class Matriz {
  15.  
  16. private:
  17.  
  18.  int nfilas;
  19.  int ncolumnas;
  20.  T **matr = NULL;
  21.  
  22.  void borrar();
  23.  
  24. public:
  25.  
  26.  //Constructora
  27.  Matriz(int filas, int columnas);
  28.  
  29.  //Constructora-copiadora
  30.  Matriz(const Matriz& m);
  31.  
  32.  //Destructora
  33.  ~Matriz();
  34.  
  35.  //Consultoras
  36.  int filas() const;
  37.  int columnas() const;
  38.  T consultar(int fila, int columna) const;
  39.  
  40.  //Modificadora
  41.  void modificar(int fila, int columna, T x);
  42.  
  43.  //Operadores
  44.  Matriz<T>& operator =(const Matriz &b);
  45.  
  46.  void operator +=(const Matriz &mat);
  47.  void operator -=(const Matriz &mat);
  48.  
  49.  Matriz<T> operator +(const Matriz &b) const;
  50.  Matriz<T> operator -(const Matriz &b) const;
  51.  Matriz<T> operator *(const Matriz &b) const;
  52.  
  53.  Matriz<T> operator *(int b) const;
  54. //Matriz<T> operator *(int a, const Matriz &b); Está declarada como pública y externa a la clase
  55.  
  56.  bool operator ==(const Matriz &b) const;
  57.  bool operator !=(const Matriz &b) const;
  58.  
  59.  friend ostream& operator << <>(ostream &salida, const Matriz<T> &mat);
  60.  friend istream& operator >> <>(istream &entrada, Matriz<T> &mat);
  61. };
  62.  
  63. template <class T>
  64. Matriz<T>::Matriz(int filas = 0, int columnas = 0) : nfilas(filas), ncolumnas(columnas)
  65. {
  66.  if (nfilas > 0 and ncolumnas > 0)
  67.  {
  68.    matr = new T* [filas];
  69.    for (int i = 0; i < filas; ++i) matr[i] = new T [columnas];
  70.  }
  71. }
  72.  
  73. template <class T>
  74. Matriz<T>::Matriz(const Matriz &m)
  75. {
  76.  *this = m;
  77. }
  78.  
  79. template <class T>
  80. Matriz<T>::~Matriz()
  81. {
  82.  borrar();
  83. }
  84.  
  85. template <class T>
  86. void Matriz<T>::borrar()
  87. {
  88.  if (this->matr != NULL)
  89.  {
  90.    for (int i = 0; i < nfilas; ++i) delete[] matr[i];
  91.    delete[] matr;
  92.    matr = NULL;
  93.  }
  94. }
  95.  
  96. template <class T>
  97. int Matriz<T>::filas() const
  98. {
  99.  return nfilas;
  100. }
  101.  
  102. template <class T>
  103. int Matriz<T>::columnas() const
  104. {
  105.  return ncolumnas;
  106. }
  107.  
  108. template <class T>
  109. T Matriz<T>::consultar(int fila, int columna) const
  110. {
  111.  return matr[fila][columna];
  112. }
  113.  
  114. template <class T>
  115. void Matriz<T>::modificar(int fila, int columna, T x)
  116. {
  117.  matr[fila][columna] = x;
  118. }
  119.  
  120. template <class T>
  121. Matriz<T>& Matriz<T>::operator =(const Matriz &mat)
  122. {
  123.  if (this != &mat)
  124.  {
  125.    this->borrar();
  126.  
  127.    if (mat.matr)
  128.    {
  129.      this->nfilas = mat.nfilas;
  130.      this->ncolumnas = mat.ncolumnas;
  131.  
  132.      this->matr = new T* [nfilas];
  133.      for (int i = 0; i < nfilas; ++i)
  134.        this->matr[i] = new T [ncolumnas];
  135.  
  136.      for (int i = 0; i < nfilas; ++i)
  137.      {
  138.        for (int j = 0; j < ncolumnas; ++j)
  139.          this->matr[i][j] = mat.matr[i][j];
  140.      }
  141.    }
  142.  }
  143.  return *this;
  144. }
  145.  
  146. template <class T>
  147. void Matriz<T>::operator +=(const Matriz &mat)
  148. {
  149.  if (this->nfilas != 0 and this->ncolumnas != 0 and mat.nfilas != 0 and mat.ncolumnas != 0)
  150.  {
  151.    for (int i = 0; i < nfilas; ++i)
  152.      for (int j = 0; j < ncolumnas; ++j)
  153.        this->matr[i][j] += mat.matr[i][j];
  154.  }
  155. }
  156.  
  157. template <class T>
  158. void Matriz<T>::operator -=(const Matriz &mat)
  159. {
  160.  if (this->nfilas != 0 and this->ncolumnas != 0 and mat.nfilas != 0 and mat.ncolumnas != 0)
  161.  {
  162.    for (int i = 0; i < nfilas; ++i)
  163.      for (int j = 0; j < ncolumnas; ++j)
  164.        this->matr[i][j] -= mat.matr[i][j];
  165.  }
  166. }
  167.  
  168. template <class T>
  169. Matriz<T> Matriz<T>::operator +(const Matriz &b) const
  170. {
  171.  int filas = this->nfilas;
  172.  int columnas = this->ncolumnas;
  173.  
  174.  Matriz<T> res(filas, columnas);
  175.  
  176.  for (int i = 0; i < filas; ++i)
  177.    for (int j = 0; j < columnas; ++j)
  178.      res.matr[i][j] = this->matr[i][j] + b.matr[i][j];
  179.  
  180.  return res;
  181. }
  182.  
  183. template <class T>
  184. Matriz<T> Matriz<T>::operator -(const Matriz &b) const
  185. {
  186.  int filas = this->nfilas;
  187.  int columnas = this->ncolumnas;
  188.  
  189.  Matriz<T> res(filas, columnas);
  190.  
  191.  for (int i = 0; i < filas; ++i)
  192.    for (int j = 0; j < columnas; ++j)
  193.      res.matr[i][j] = this->matr[i][j] - b.matr[i][j];
  194.  
  195.  return res;
  196. }
  197.  
  198. template <class T>
  199. Matriz<T> Matriz<T>::operator *(const Matriz &b) const
  200. {
  201.  if (this->ncolumnas == b.nfilas)
  202.  {
  203.    int pos = this->ncolumnas;
  204.  
  205.    int filas = this->nfilas;
  206.    int columnas = b.ncolumnas;
  207.  
  208.    Matriz<T> res(filas, columnas);
  209.    for (int i = 0; i < filas; ++i)
  210.    {
  211.      for (int j = 0; j < columnas; ++j)
  212.      {
  213.        T value = 0;
  214.        for (int k = 0; k < pos; ++k) value += this->matr[i][k] * b.matr[k][j];
  215.        res.matr[i][j] = value;
  216.      }
  217.    }
  218.    return res;
  219.  }
  220. }
  221.  
  222. template <class T>
  223. Matriz<T> Matriz<T>::operator *(int b) const
  224. {
  225.  int filas = this->nfilas;
  226.  int columnas = this->ncolumnas;
  227.  
  228.  Matriz res(filas, columnas);
  229.  for (int i = 0; i < filas; ++i)
  230.    for (int j = 0; j < columnas; ++j) res.matr[i][j] = this->matr[i][j] * b;
  231.  
  232.  return res;
  233. }
  234.  
  235. template <class T>
  236. Matriz<T> operator *(int a, const Matriz<T> &b)
  237. {
  238.  return b*a;
  239. }
  240.  
  241. template <class T>
  242. bool Matriz<T>::operator ==(const Matriz &b) const
  243. {
  244.  if (this->nfilas != b.nfilas or this->ncolumnas != b.ncolumnas) return false;
  245.  for (int i = 0; i < this->nfilas; ++i)
  246.    for (int j = 0; j < this->ncolumnas; ++j)
  247.      if (this->matr[i][j] != b.matr[i][j]) return false;
  248.  
  249.  return true;
  250. }
  251.  
  252. template <class T>
  253. bool Matriz<T>::operator !=(const Matriz<T> &b) const
  254. {
  255.  if (this->nfilas != b.nfilas or this->ncolumnas != b.ncolumnas) return true;
  256.  for (int i = 0; i < this->nfilas; ++i)
  257.    for (int j = 0; j < this->ncolumnas; ++j)
  258.      if (this->matr[i][j] != b.matr[i][j]) return true;
  259.  
  260.  return false;
  261. }
  262.  
  263. template <class T>
  264. ostream& operator <<(ostream &salida, const Matriz<T> &mat)
  265. {
  266.  for (int i = 0; i < mat.nfilas; ++i)
  267.  {
  268.    for (int j = 0; j < mat.ncolumnas; ++j) salida << mat.matr[i][j] << " ";
  269.    salida << endl;
  270.  }
  271.  return salida;
  272. }
  273.  
  274. template <class T>
  275. istream& operator >>(istream &entrada, Matriz<T> &mat)
  276. {
  277.  for (int i = 0; i < mat.nfilas; ++i)
  278.    for (int j = 0; j < mat.ncolumnas; ++j) entrada >> mat.matr[i][j];
  279.  return entrada;
  280. }

Pues bien, como podéis comprobar he arreglado el problema que tenia con las funciones friend y los templates implementando las sobrecargas como métodos de la clase y añadiendo una pre-especificación que está comentada con un enlace, dónde se explica la solución al error de las sobrecargas de iostream (parece que el problema venía por temas internos de la clase iostream que no interpretaba bien operaciones internas de los templates). La sobrecarga del operador de un entero por una matriz he tenido que declararlo como externo a la clase, ya que si se declara como método cogerá el parámetro implícito como primer parámetro de la operación.

El resto de cambios son por la implementación de los templates.

Si alguien tiene alguna duda o sugerencia, será bienvenida ^^

Saludos y gracias amchacon por toda la ayuda!

PD: Iré mejorando la clase, como ya dije quiero implementar Gauss y varias cosas más, pero de momento voy a centrarme en el estudio de punteros que para mitades de enero tengo el examen final de programación y quiero mi matricula  ;-)