[C++] Costruttore copia di uno stack

di il
2 risposte

[C++] Costruttore copia di uno stack

Abbiamo iniziato gli stack la settimana scorsa e la prof. ci ha detto di scrivere l'interfaccia base della classe (costruttori, push e pop) utilizzando come classe aggregata un single-linked node (che abbiamo già utilizzato per creare le liste).
il mio problema è nel creare il costruttore copia: da quanto ho capito bisogna passare lo stack da copiare per copia (e quindi il ragionamento è sbagliato già da qui), copiare il contenuto dello stack dentro uno stack d'appoggio (altrimenti si avrebbero gli elementi in disposizione esattamente inversa rispetto allo stack di partenza) e poi buttare questo stack d'appoggio nello stack appena costruito (this).
appunto però, da come l'ho pensato io, vi è la necessità di passare lo stack da copiare per copia, altrimenti verrebbe modificato se lo passassi come reference (anzi, diverrebbe uno stack vuoto).
non so quindi come attuare un procedimento logico: mi è subito venuto in mente il classico scorrimento che si effettua con le liste (quello con il puntatore d'appoggio), ma non sarebbe illogico? in una pila non si possono "scorrere" gli elementi, bisogna togliere ogni volta quello in cima...
qualcuno mi saprebbe spiegare come fare un costruttore copia di uno stack? (che seguendo questo ragionamento sarebbe obbligatorio: un altro dei metodi da implementare era appunto la scansione: anche in quel caso devo passare la pila per copia... e serve appunto il costruttore copia).

la mia classe è questa:
stack.h:
#pragma once
#include "../Lista Template/nodo.cpp"

template <class T>
class Stack
{
protected:
	Nodo<T>* top;

public:
	Stack();
	Stack(const T& val);
	Stack(Stack<T> copy);

	~Stack();
	Nodo<T>* getTop(void);

	void push(const T& val);
	T pop(void);

	bool isEmpty(void);

	friend std::ostream& operator<<(std::ostream& output, Stack<T> stack)
	{
		if (stack.isEmpty())
			output << "Pila vuota." << endl;
		else
		{
			while (stack.getTop() != nullptr)
				output << stack.pop() << endl;
		}

		return output;
	}
};
stack.cpp:
#include "stack.h"

template <typename T>
Stack<T>::Stack()
{
	top = nullptr;
}

template <typename T>
Stack<T>::Stack(const T& val)
{
	top = new Nodo<T>(val);
}

template <typename T>
Stack<T>::Stack(Stack<T> copy) // non funzionante
{
	top = nullptr;

	Stack<T> temp;

	while (copy.getTop() != nullptr)
		temp.push(copy.pop());

	this->push(temp.pop());
}

template <typename T>
Stack<T>::~Stack()
{
	while (top != nullptr)
		pop();
}

template <typename T>
Nodo<T>* Stack<T>::getTop(void)
{
	return top;
}

template <typename T>
void Stack<T>::push(const T& val)
{
	Nodo<T>* aux = new Nodo<T>(val, top);
	top = aux;
}

template <typename T>
T Stack<T>::pop(void)
{
	const T val = top->getValue();

	Nodo<T>* aux = top->getNext();
	delete top;
	top = aux;

	return val;
}

template <typename T>
bool Stack<T>::isEmpty(void)
{
	return (top == nullptr);
}
nodo.h:
#pragma once

template <typename T>
class Node
{
protected:
	T value; // valore contenuto all'interno del nodo
	Node<T>* next; // puntatore all'elemento successivo

public:
	/* Costruttore di default. */
	Node();

	/* Costruttore con parametro l'informazione del nodo. */
	Node(const T& info);

	/* Costruttore con parametri. */
	Node(const T& info, Node<T>* next);

	/* Costruttore copia. */
	Node(const Node<T>& copy);

	// Imposta il valore di T.
	void setValue(const T& value);

	// Imposta il puntatore al nodo successivo.
	void setNext(Node<T>* next);

	// Ottiene il valore del nodo.
	T getValue(void);

	// Ottiene il puntatore al nodo successivo.
	Node<T>* getNext(void);
};
nodo.cpp:
#include "node.h"

template <typename T>
Node<T>::Node() : value()
{
	next = nullptr;
}

template <typename T>
Node<T>::Node(const T& info)
{
	value = info;
	next = nullptr;
}

template <typename T>
Node<T>::Node(const T& info, Node<T>* next)
{
	value = info;
	this->next = next;
}

template <typename T>
Node<T>::Node(const Node<T>& copy)
{
	value = copy.value;
	next = copy.next;
}

template <typename T>
void Node<T>::setValue(const T& value)
{
	this->value = value;
}

template <typename T>
void Node<T>::setNext(Node<T>* next)
{
	this->next = next;
}

template <typename T>
T Node<T>::getValue(void)
{
	return value;
}

template <typename T>
Node<T>* Node<T>::getNext(void)
{
	return next;
}

2 Risposte

  • Re: [C++] Costruttore copia di uno stack

    Il guaio è che se passi per copia qualcosa al costruttore di copia, quello deve fare una copia e per fare una copia deve invocare il costruttore di copia che per fare una copia deve invocare il costruttore di copia che per fare... stack overflow!.
    ma non sarebbe illogico? in una pila non si possono "scorrere" gli elementi, bisogna togliere ogni volta quello in cima...
    vero per uno stack, ma per quanto riguarda l'aggregato?
    Personalmente risolverei tenendo un puntatore al primo elemento dello stack; puntatore usato SOLO come osservatore, impostato durante il primo inserimento dello stack e non più modificabile (ovviamente privato).
    
    template <typename T>
    Stack<T>::Stack()
    {
       top = nullptr;
       observer_first = nullptr;
    }
    
    template <typename T>
    Stack<T>::Stack(const T& val)
    {
        this->push(val);
    }
    
    template <typename T>
    Stack<T>::Stack(const Stack<T>& copy) {
    
        const Node<T>* aux = copy.observer_first;  
        if (aux  != nullptr) {
            while (aux->next != nullptr) {
                this->push(*aux);
                aux = aux->next; 
            }
        }
    }
    
    template <typename T>
    void Stack<T>::push(const T& val)
    {
       Nodo<T>* aux = new Nodo<T>(val, top);
       top = aux;
       if (observer_ptr == nullptr) // singola assegnazione
           observer_ptr = top;
    }
    
    Più o meno qualcosa di simile (al netto di eventuali controllo errori)
  • Re: [C++] Costruttore copia di uno stack

    shodan ha scritto:


    Il guaio è che se passi per copia qualcosa al costruttore di copia, quello deve fare una copia e per fare una copia deve invocare il costruttore di copia che per fare una copia deve invocare il costruttore di copia che per fare... stack overflow!.
    ma non sarebbe illogico? in una pila non si possono "scorrere" gli elementi, bisogna togliere ogni volta quello in cima...
    vero per uno stack, ma per quanto riguarda l'aggregato?
    Personalmente risolverei tenendo un puntatore al primo elemento dello stack; puntatore usato SOLO come osservatore, impostato durante il primo inserimento dello stack e non più modificabile (ovviamente privato).
    
    template <typename T>
    Stack<T>::Stack()
    {
       top = nullptr;
       observer_first = nullptr;
    }
    
    template <typename T>
    Stack<T>::Stack(const T& val)
    {
        this->push(val);
    }
    
    template <typename T>
    Stack<T>::Stack(const Stack<T>& copy) {
    
        const Node<T>* aux = copy.observer_first;  
        if (aux  != nullptr) {
            while (aux->next != nullptr) {
                this->push(*aux);
                aux = aux->next; 
            }
        }
    }
    
    template <typename T>
    void Stack<T>::push(const T& val)
    {
       Nodo<T>* aux = new Nodo<T>(val, top);
       top = aux;
       if (observer_ptr == nullptr) // singola assegnazione
           observer_ptr = top;
    }
    
    Più o meno qualcosa di simile (al netto di eventuali controllo errori)
    grazie mille, davvero!
    oggi abbiamo effettuato la correzione in classe dei compiti e, come pensavo, l'implementazione di questo costruttore dipende dal programmatore (se copiarla direttamente, copiarla invertita, senza quindi la pila d'appoggio, oppure ancora se copiarla andando a vuotare la pila passata). in moltissimi scorrevano la pila con il classico metodo come se fosse una lista, mentre io avevo in mente questa cosa che mi sembra più logica (è stato infatti ribadito che non possiamo scorrere la pila come se fosse una lista).
    per questo motivo abbiamo creato un costruttore copia che modificava la pila passata. il ragionamento era identico a ciò che avevo pensato io, però la pila veniva passata come reference (non costante, poiché al termine del costruttore diventava vuota).
    personalmente ho capito bene l'algoritmo, ma penso che si vada ad alterare il concetto di costruttore copia: deve eseguire un "brutale" copia-incolla dell'oggetto, mentre in questo caso effettua un taglia-incolla. tuttavia la prof. ci ha detto che con le pile basate sui single-linked node è l'unico modo per effettuare una copia di una pila rispettando la logica della struttura dati, penso per il fatto che si presuppone che il costruttore copia sia sempre chiamato implicitamente (per esempio quando si passa/ritorna una pila come copia) piuttosto che richiamarlo esplicitamente, facendo per esempio:
    Stack<int> s1;
    Stack<int> s2(s1);
    ti ringrazio ancora una volta per l'aiuto
Devi accedere o registrarti per scrivere nel forum
2 risposte