1

Debo armar una lista doblemente enlazada, pero se me presentó un error que no puedo solucionar:

Clase nodo
Es importante destacar que esta clase hace uso de template para aceptar cualquier formato.

#ifndef nodo_h
#define nodo_h
#include <iostream>
using namespace std;

template<class type>
class nodo
{
private:
    type element;
    nodo *next, *previous;
public:
    void SetElement(type element);
    void ShowElement();
    type GetElement();
    nodo<type>* GetNext();
    nodo<type>* GetPrevious();
    nodo<type>();
    ~nodo<type>();

};

Clase lista8
Los problemas se presentan en los procedimientos SetFirst y SetLast
olvidé decir que esta lista debe también usa template para poder enviar variables desde main, osea que accede primero a lista8 donde hay una lista aún no definida de la clase nodo

#ifndef lista8_h
#define lista8_h
#include "nodo.h"

template<class type>
class lista8
{
private:
    nodo<type> *first, *last;
public:
    void SetFirst(type NewElement); //<-error
    void SetLast(type NewElement); //<-error
    int ClearFirst();
    int ClearLast();
    void ShowList(nodo<type> *list);
    nodo<type>* GetFirst();
    nodo<type>* GetLast();
    lista8<type>();
    ~lista8<type>();
};

Aqui está el código de SetFirst y SetLast, incluyendo donde está el error,
el error que visual studio me muestra es lo que dice en el título.
(siendo más específicos, dice: C2106 '=': el operando debe ser valor L)
Donde dice 'error que esperaba' es donde imaginé que también aparecería éste error, pero curiosamente, no me dice que sea incorrecto.

template<class type>
void lista8<type>::SetFirst(type NewElement)
{
    if (first == NULL)
    {
        first = new nodo<type>; //<--error que esperaba
        last = first;
    }
    else
    {
        first->GetPrevious() = new nodo<type>; //<--error
        first->GetPrevious()->GetNext() = first; //<--error
        first = first->GetPrevious();
    }
    first->SetElement(NewElement);
}

template<class type>
void lista8<type>::SetLast(type NewElement)
{
    if (last == NULL)
    {
        last = new nodo<type>; //<--error que esperaba
        first = last;
    }
    else
    {
        last->GetNext() = new nodo<type>; //<--error
        last->GetNext()->GetPrevious() = last; //<--error
        last = last->GetNext();
    }
    last->SetElement(NewElement);
}
Max
  • 320
  • 1
  • 3
  • 13

2 Answers2

1

Te recomiendo, como lectura inicial, esta otra pregunta.

Si yo tengo esto:

struct POO
{
  POO() : a(0)
  { }

  int func()
  { return a; }

  int a;
};

int main()
{
  POO p;
  p.func() = 10;
  std::cout << p.a;
}

¿Crees que funcionará?

Obviamente no debería, al menos no como se espera viendo la intención del código. func devuelve una copia de a, luego la asignación p.func() = 10 dificilmente podrá modificar el valor de a.

Pues con los punteros pasa exactamente lo mismo. Un puntero es una variable cuya característica principal es que almacena una dirección de memoria. Cuando tu copias un puntero:

int a;
int* ptr = &a;
int* ptr2 = ptr; // <<---

Lo que sucede es que la dirección de memoria se copia en el nuevo puntero. Así ambos punteros tienen almacenada la misma dirección de memoria y, por tanto, los cambios en dicha memoria serán visibles para ambos punteros. El detalle aquí es que hemos copiado la dirección de memoria, si después hacemos que uno de los dos punteros apunte a otra región diferente el otro puntero ni se enterará:

int a = 1, b = 2;
int* ptr1 = &a;
int* ptr2 = ptr1;
std::cout << *ptr1 << *ptr2 << '\n'; // Imprime 11
ptr2 = &b;
std::cout << *ptr1 << *ptr2 << '\n'; // Imprime 12

Pues bien, tu lo que estás pretendiendo en esta línea

first->GetPrevious() = new nodo<type>;

Es que se actualice el puntero interno previous. Sin embargo GetPrevious devuelve una copia de dicho puntero y, como hemos visto, a partir de una copia es imposible modificar el elemento original.

Lo más limpio es que añadas un SetPrevious que permita modificar el puntero:

template<class type>
class nodo
{
private:
    type element;
    nodo *next, *previous;
public:
    void SetElement(type element);
    void ShowElement();
    type GetElement();
    nodo* GetNext();
    nodo* GetPrevious();

    void SetPrevious(nodo*);
};
eferion
  • 49,291
  • 5
  • 30
  • 72
  • Gracias, tienes razón, es mucho más efectivo y eficiente que tener que recuperar la dirección de memoria, y solo una duda más, mi docente me recomendó que usara una técnica la cual funciona también, pero que la verdad me hace desconfiar un poco, no me gusta que puedan haber errores en mi código, me recomendó hacer esto: nodo*& GetPrevious(); Algo que curiosamente, hizo que mi código se arreglara, mi duda es ¿puedo confiar en esa técnica? – Max Aug 17 '18 at 00:46
  • @Max si bien con ese cambio funcionaría, la solución daría como resultado una pérdida de encaminamiento, ya que no tendrías forma alguna de controlar quién, cómo y cuando modifica el valor de una variable miembro que es tuya. Al no poder controlarla podrían asignar valores no válidos, lo que dejaría tu clase inconsistente y, al no controlar quien y cómo accede, se complica en exceso el mantenimiento del código y la localización de errores – eferion Aug 17 '18 at 04:40
0

@Max la solución que te han propuesto no hace magia, simplemente estás devolviendo una referencia al puntero... es como si la función devolviese un puntero doble (parecido). Esta solución la usa mucho la STL... pero únicamente con referencias constantes, es decir, que no podrías modificar su valor salvo que uses técnicas para nada recomendadas

eferion
  • 49,291
  • 5
  • 30
  • 72