Esercizi di C++
scritti da Lanfranco Lopriore e risolti da Francesco Potortì

Quelli che seguono sono testi d'esame creati dal Prof. Lanfranco Lopriore per il corso di Fondamenti di Informatica del 1º anno del corso di laurea in Ingegneria Informatica presso l'Università di Pisa durante l'anno accademico 1997-98. Per ognuno di essi è proposta una soluzione sotto forma di file sorgente C++. Le soluzioni sono state scritte da me, Francesco Potortì, e sono state compilate utilizzando sia il compilatore Visual C++ della Microsoft, sia il compilatore g++ sviluppato nell'ambito del progetto GNU.

I file usano il suffisso .cc, che è lo standard per GNU make. Può esser comodo usare questo makefile per compilare i sorgenti. GNU make per ambienti Microsoft può essere trovato a http://www.delorie.com/djgpp/, assieme ad altri programmi GNU. In alternativa, per far sì che il Visual C++ riconosca automaticamente i file come sorgenti C++, conviene cambiare il loro suffisso in .cpp.

Nelle prime stesure dello standard, la new poteva ritornare un valore 0, che indicava fallimento, per cui risultava necessario controllare ogni valore di ritorno, o meglio utilizzare la set_new_handler. Nelle soluzioni proposte suppongo di utilizzare un compilatore non obsoleto che, come previsto dalla versione definitiva dello standard, generi un'eccezione quando la new fallisce. Non utilizzo la set_new_handler per facilitare la portabilità al Visual C++ che, ignorando lo standard, non la implementa, o meglio ne implementa solo uno stub non funzionale, che compila ma genera un errore all'esecuzione.

La soluzione del primo esercizio può essere compilata per produrre un file eseguibile. Benché il testo richieda solo l'implementazione del tipo di dati, per effettuare un controllo completo della correttezza del codice e rendersi pienamente conto del suo funzionamento è necessario scrivere un programmino che implementi un main, il quale utilizzi le classi implementate. Tutte le altre soluzioni comprendono un singolo file contenente solo le dichiarazioni e definizioni richieste dalle tracce di esame, e pertanto si possono compilare in un file oggetto che non può girare. Per ottenerne dei programmi eseguibili, bisognerà scrivere un main sul modello del primo esercizio.

La maniera migliore di utilizzare il materiale qui contenuto consiste nel cercare di eseguire le tracce senza prima leggere le soluzioni proposte. Prima di scrivere anche una sola riga di codice, è importante aver letto attentamente e con calma il testo per almeno due volte. La cosa più importante per una corretta soluzione è la scelta delle strutture dati che saranno utilizzate. Una scelta oculata consente di scrivere la minima quantità di codice, e di ridurre la possibilità di errori.

Le soluzioni che propongo sono state esaminate accuratamente, ma naturalmente qualche errore può essermi sfuggito. Prego chi se ne dovesse accorgere di segnalarmelo, scrivendomi un messaggio. Chiunque è libero di utilizzare e riprodurre questi sorgenti come meglio crede, purché nelle eventuali pubblicazioni sia citato il mio nome.

ListaMultipla
Testo e soluzione proposta del compito d'esame del 21 aprile 1998 (dichiarazioni, definizioni e main).
Elemento e StackDiElementi
Testo e soluzione proposta del compito d'esame del 5 giugno 1998.
Dado e LancioDiDadi
Testo e soluzione proposta del compito d'esame del 26 giugno 1998.
InsiemeDiInteri e Collezione
Testo e soluzione proposta del compito d'esame del 17 luglio 1998.
Striscia
Testo e soluzione proposta del compito d'esame del 15 settembre 1998.
Muro
Testo e soluzione proposta del compito d'esame del 25 settembre 1998. A causa di un baco del compilatore MS VisualC++ nelle versioni fino alla 6.0 (dettagli), la soluzione proposta non compila. Una versione leggermente modificata compila invece regolarmente.

Compito d'esame del 21 aprile 1998: ListaMultipla

Una lista di interi è formata da elementi a valore intero. Il peso di una lista di interi è dato dalla somma dei valori interi contenuti nella lista stessa. Una lista multipla è formata da al più T liste di interi. Le operazioni che possono essere effettuate su una lista multipla sono le seguenti:

ListaMultipla m()
Costruttore di default, che inizializza una lista multipla m. Inizialmente, tale lista multipla è vuota.
ListaMultipla m(n)
Costruttore che inizializza una lista multipla formata da n liste di interi. Inizialmente, tali liste di interi sono vuote.
ListaMultipla m1(m)
Costruttore di copia, che inizializza una lista multipla m1 col valore della lista multipla m.
m1 = m
Operatore di assegnamento, che sostituisce il valore della lista multipla risultato m1 con quello della lista multipla m.
m.inserisci(i)
Operazione che inserisce un elemento a valore intero i nella lista di interi a minor peso della lista multipla m.
m.estrai()
Operazione che estrae un elemento a valore intero dalla lista di interi a maggior peso della lista multipla m, e ritorna il valore di tale elemento.
m += n
Operatore di somma e assegnamento, che aggiunge n liste di interi alla lista multipla m. Tali liste di interi sono vuote.
m -= n
Operatore di sottrazione e assegnamento, che elimina dalla lista multipla m le n liste di interi aventi peso maggiore.
~ListaMultipla()
Distruttore.
cout << m
Operatore di uscita per il tipo ListaMultipla. L'uscita ha la forma seguente: [1, 2, 1] [2, 4, 3] [15] [2, 19]. Le parentesi quadre indicano una lista di interi. Le liste di interi vengono scritte secondo pesi crescenti. In questo esempio, la lista multipla è formata da quattro liste di interi; la terza di tali liste di interi è formata da un solo elemento avente valore 15.

Utilizzando il linguaggio C++, realizzare il tipo di dati astratti ListaMultipla definito dalle precedenti specifiche. Individuare eventuali situazioni di errore, e metterne in opera un corretto trattamento.


Compito d'esame del 5 giugno 1998: Elemento e StackDiElementi

Un Elemento ha un peso di tipo intero e uno stato trasparente od opaco. Le operazioni che possono essere effettuate su un elemento sono le seguenti:

Elemento e()
Costruttore di default, che inizializza un elemento e. Inizialmente, tale elemento è trasparente ed ha peso 0.
Elemento e(s, n)
Costruttore che inizializza un elemento e. Inizialmente, tale elemento ha stato s e peso n.
Elemento e1(e)
Costruttore di copia, che inizializza un elemento e1 col valore dell'elemento e.
e1 = e
Operatore di assegnamento, che sostituisce il valore dell'elemento risultato e1 con quello dell'elemento e.
recente()
Operazione che ritorna il valore di creazione dell'elemento creato più di recente.
e.peso()
Operazione che ritorna il peso dell'elemento e.
e.stato()
Operazione che ritorna lo stato dell'elemento e.
cout << e
Operatore di uscita per il tipo Elemento. L'uscita specifica lo stato, 'T' per trasparente ed 'O' per opaco, ed il peso dell'elemento, separati dal carattere ':'. Esempio: T:100.
cin >> e
Operatore di ingresso per il tipo Elemento. L'operatore legge il risultato dell'operatore di uscita per il tipo Elemento.
~Elemento()
Distruttore.

Uno StackDiElementi è in grado di contenere elementi in numero limitato (la capacità dello stack di elementi). Le operazioni che possono essere effettuate su uno stack di elementi sono le seguenti:

StackDiElementi t()
Costruttore di default, che inizializza uno stack di elementi t avente capacità pari al valore dell'intero costante M. Inizialmente, tale stack di elementi è vuoto.
StackDiElementi t(n)
Costruttore che inizializza uno stack di elementi t avente capacità pari a n elementi. Inizialmente, tale stack di elementi è vuoto.
StackDiElementi t1(t)
Costruttore di copia, che inizializza uno stack di elementi t1 col valore dello stack di elementi t.
t1 = t
Operatore di assegnamento, che sostituisce il valore dello stack di elementi risultato t1 con quello dello stack di elementi t.
t.push()
Operazione che inserisce nello stack di elementi t un elemento avente valore pari al valore di creazione dell'elemento creato più di recente. L'operazione opera in accordo con la strategia LIFO.
t.push(e)
Operazione che inserisce nello stack di elementi t un elemento avente valore pari al valore dell'elemento e. L'operazione opera in accordo con la strategia LIFO.
t.pop(s)
Operazione che estrae dallo stack di elementi t il primo elemento di stato s e ritorna il valore di tale elemento. L'operazione opera in accordo con la strategia LIFO.
t.top(s)
Operazione che ritorna il valore del primo elemento di stato s dello stack di elementi t. L'operazione opera in accordo con la strategia LIFO.
t.vuoto()
Operazione che ritorna l'intero 1 se lo stack di elementi t è vuoto, e 0 altrimenti.
t.pieno()
Operazione che ritorna l'intero 1 se lo stack di elementi t è pieno, e 0 altrimenti.
~StackDiElementi()
Distruttore.

Mediante il linguaggio C++, realizzare il tipo di dati astratti Elemento, definito dalle precedenti specifiche. Utilizzare il tipo di dati astratti Elemento per realizzare il tipo di dati astratti StackDiElementi facendo ricorso ad una rappresentazione dello stack di elementi ad array. Individuare le eventuali situazioni di errore, e metterne in opera un corretto trattamento.


Compito d'esame del 26 giugno 1998: Dado e LancioDiDadi

Un Dado ha valore intero compreso tra 1 e 6. Le operazioni che possono essere effettuate su un dado sono le seguenti:

Dado d()
Costruttore di default, che inizializza un dado d. Inizialmente, tale dado ha valore 1.
Dado d1(d)
Costruttore di copia, che inizializza un dado d1 col valore del dado d.
Dado d(n)
Costruttore di conversione, che consente di usare un int ovunque occorre un dado. Il costruttore inizializza il dado d col valore intero n.
operator int(d)
Operatore di conversione, che consente di usare un dado ovunque occorre un int. L'operatore ritorna il valore del dado d.
d1 = d
Operatore di assegnamento, che sostituisce il valore del dado risultato d1 con quello del dado d.
~Dado()
Distruttore.

Un LancioDiDadi è in grado di contenere un numero limitato di dadi. Le operazioni che possono essere effettuate su un lancio di dadi sono le seguenti:

LancioDiDadi c()
Costruttore di default, che inizializza un lancio di dadi c. Tale lancio di dati può contenere al più un numero di dadi pari al valore dell'intero costante M. Inizialmente, il lancio di dati non contiene dadi.
LancioDiDadi c1(c)
Costruttore di copia, che inizializza un lancio di dadi c1 col valore del lancio di dadi c.
LancioDiDadi c(n)
Costruttore di conversione, che consente di usare un int ovunque occorre un lancio di dadi. Il costruttore inizializza un lancio di dadi c. Tale lancio di dati può contenere al più n dadi. Inizialmente, il lancio di dati non contiene dadi.
operator int(c)
Operatore di conversione, che consente di usare un lancio di dadi ovunque occorre un int. L'operatore ritorna la somma dei valori dei dadi che formano il lancio di dadi c.
c1 = c
Operatore di assegnamento, che sostituisce il valore del lancio di dadi risultato c1 con quello del lancio di dadi c.
c += d
Operatore di somma e assegnamento, che aggiunge il dado d al lancio di dadi c.
c -= d
Operatore di sottrazione e assegnamento, che elimina il dado d dal lancio di dadi c.
c %= d
Operatore di modulo e assegnamento, che elimina dal lancio di dadi c tutti i dadi aventi valore pari al dado d.
!c
Operatore di negazione, che ritorna il numero di dadi contenuti nel lancio di dadi c.
cout << c
Operatore di uscita per il tipo LancioDiDadi. L'uscita consiste nei valori dei dadi che formano il lancio c, in ordine crescente, separati da virgole e racchiusi tra parentesi graffe. Esempio: {1, 1, 1, 4, 5}. In questo esempio, il lancio è formato da cinque dadi, ed i primi tre hanno valore 1.
~LancioDiDadi()
Distruttore.

Mediante il linguaggio C++, realizzare il tipo di dati astratti Dado, definito dalle precedenti specifiche. Utilizzare il tipo Dado per realizzare il tipo di dati astratti LancioDiDadi. Individuare le eventuali situazioni di errore, e metterne in opera un corretto trattamento.


Compito d'esame del 17 luglio 1998: InsiemeDiInteri e Collezione

Un InsiemeDiInteri è formato interi diversi compresi tra 0 ed N-1, con N minore o uguale al numero di bit che formano un long int. Le operazioni che possono essere effettuate su un insieme di interi sono le seguenti:

InsiemeDiInteri d()
Costruttore di default, che inizializza un insieme di interi d. Inizialmente, tale insieme di interi è vuoto.
InsiemeDiInteri d1(d)
Costruttore di copia, che inizializza un insieme di interi d1 col valore dell'insieme di interi d.
operator int(d)
Operatore di conversione, che consente di usare un insieme di interi ovunque occorre un int. L'operatore ritorna la somma degli interi che formano l'insieme di interi d.
d1 = d
Operatore di assegnamento, che sostituisce il valore dell'insieme di interi risultato d1 con quello dell'insieme di interi d.
d += i
Operatore di somma e assegnamento, che aggiunge l'intero i all'insieme di interi d.
d -= i
Operatore di sottrazione e assegnamento, che elimina l'intero i dall'insieme di interi d.
cout << d
Operatore di uscita per il tipo InsiemeDiInteri. L'uscita consiste nei valori degli interi che formano l'insieme di interi d, in ordine crescente, separati da virgole e racchiusi tra parentesi graffe. Esempio: {1, 3, 8, 14}.
~InsiemeDiInteri()
Distruttore.

Un Collezione è formata da insiemi di interi in numero limitato (la capienza della collezione). Le operazioni che possono essere effettuate su una collezione sono le seguenti:

Collezione c()
Costruttore di default, che inizializza una collezione c avente capienza pari al valore dell'intero costante M. Inizialmente, tale collezione è vuota.
Collezione c(m)
Costruttore di default, che inizializza una collezione c avente capienza m. Inizialmente, tale collezione è vuota.
Collezione c1(c)
Costruttore di copia, che inizializza una collezione c1 col valore della collezione c.
operator int(c)
Operatore di conversione, che consente di usare una collezione ovunque occorre un int. L'operatore ritorna la somma degli interi che formano gli insieme di interi contenuti in c.
c1 = c
Operatore di assegnamento, che sostituisce il valore della collezione risultato c1 con quello della collezione c.
c += d
Operatore di somma e assegnamento, che aggiunge l'insieme di interi d alla collezione c.
c -= d
Operatore di sottrazione e assegnamento, che elimina l'insieme di interi d dalla collezione c.
cout << c
Operatore di uscita per il tipo Collezione. L'uscita consiste nei valori degli insiemi di interi che formano la collezione c. Esempio: {1, 3, 8, 14} {1} {0, 3, 5}. In questo esempio, il secondo insieme della collezione è formato da un solo intero avente valore 1.
~Collezione()
Distruttore.

Mediante il linguaggio C++, realizzare il tipo di dati astratti InsiemeDiInteri, definito dalle precedenti specifiche. Si ipotizzi che Utilizzare il tipo InsiemeDiInteri per realizzare il tipo di dati astratti Collezione. Individuare le eventuali situazioni di errore, e metterne in opera un corretto trattamento.


Compito d'esame del 15 settembre 1998: Striscia

Una Striscia è formata da caselle trasparenti o colorate, ed i possibili colori sono BIANCO e NERO. Le caselle sono numerate a partire da 1. Le caselle bianche occupano sempre le posizioni della striscia ai più bassi numeri d'ordine, le caselle nere occupano sempre le posizioni della striscia ai più alti numeri d'ordine. Le operazioni che possono essere effettuate su una striscia sono le seguenti:

Striscia s()
Costruttore di default, che inizializza una striscia s formata da una sola casella. Inizialmente, tale casella è trasparente.
Striscia s(n)
Costruttore che inizializza una striscia s formata da n caselle. Inizialmente, tali caselle sono trasparenti.
Striscia s1(s)
Costruttore di copia, che inizializza una striscia s1 col valore della striscia s.
s1 = s
Operatore di assegnamento, che sostituisce il valore della striscia risultato s1 con quello della striscia s.
s += c
Operatore di somma e assegnamento, che colora una casella trasparente della striscia s. Tale casella assume il colore c.
s + c
Operatore di somma, che ritorna la striscia ottenuta facendo assumere il colore c ad una casella trasparente della striscia s.
s -= c
Operatore di sottrazione e assegnamento, che rende trasparente una casella di colore c della striscia s.
s - c
Operatore di sottrazione, che ritorna la striscia ottenuta rendendo trasparente una casella di colore c della striscia s.
s *= n
Operatore di prodotto e assegnamento, che amplia la striscia s aggiungendo ad essa n caselle trasparenti.
s /= n
Operatore di divisione e assegnamento, che riduce la striscia s eliminando da essa n caselle trasparenti.
s % c
Operatore di modulo, che ritorna il numero di caselle di colore c della striscia s.
~Striscia()
Distruttore.
cout << s
Operatore di uscita per il tipo Striscia. L'uscita ha la forma seguente: [BBB^^^NNNNN]. I caratteri 'B' ed 'N' rappresentano caselle di colore BIANCO e NERO, rispettivamente, il carattere '^' rappresenta una casella trasparente.
cin >> s
Operatore di ingresso per il tipo Striscia. L'operatore legge il risultato dell'operatore di uscita per il tipo Striscia.

Utilizzando il linguaggio C++, realizzare il tipo di dati astratti Striscia, definito dalle precedenti specifiche. Si individuino le eventuali situazioni di errore, e se ne metta in opera un corretto trattamento.


Compito d'esame del 25 settembre 1998: Muro

Un Muro è formato da colonne affiancate di mattoni colorati, ed i possibili colori sono BIANCO, ROSSO, GIALLO e VERDE. Le colonne possono avere altezza diversa. Le operazioni che possono essere effettuate su un muro sono le seguenti:

Muro m()
Costruttore di default, che inizializza un muro m formato da una sola colonna. Inizialmente, tale colonna non contiene mattoni.
Muro m(n)
Costruttore che inizializza un muro m formato da n colonne. Inizialmente, tali colonne non contengono mattoni.
Muro m1(m)
Costruttore di copia, che inizializza un muro m1 col valore del muro m.
m1 = m
Operatore di assegnamento, che sostituisce il valore del muro risultato m1 con quello del muro m.
m.aggiungi(c)
Operazione che aggiunge un mattone di colore c alla colonna più bassa del muro m.
m.togli()
Operatorazione che toglie il mattone più in alto dalla colonna più alta del muro m.
~Muro()
Distruttore.
cout << m
Operatore di uscita per il tipo Muro. L'uscita ha la forma seguente: [V R R B][B V V][R G G G]. I caratteri `B', `R', `G' e `V' rappresentano mattoni di colore BIANCO, ROSSO, GIALLO e VERDE, rispettivamente. Ciascuna coppia di parentesi quadre specifica una colonna di mattoni, a partire dal mattone più in basso nella colonna stessa. In questo esempio, il muro è formato da tre colonne, la seconda delle quali contiene un mattone di colore BIANCO e, sopra di esso, due di colore VERDE.

Utilizzando il linguaggio C++, realizzare il tipo di dati astratti Muro, definito dalle precedenti specifiche. Si faccia ricorso ad una rappresentazione a lista di ciascuna colonna. Si individuino le eventuali situazioni di errore, e se ne metta in opera un corretto trattamento.


This page is  Best Viewed With Any Browser (gif 1K)