Domanda Non capisco perché questa funzione "restituisca un puntatore dalla lista"


Il libro che sto leggendo, Introduzione alle strutture dati con liste collegate (Presentazione 21), ha 2 esempi di liste collegate. Ecco il primo:

EnemySpaceShip* getNewEnemy ()
{
    EnemySpaceShip* p_ship = new EnemySpaceShip;
    p_ship->x_coordinate = 0;
    p_ship->y_coordinate = 0;
    p_ship->weapon_power = 20;
    p_ship->p_next_enemy = p_enemies;
    p_enemies = p_ship;
    return p_ship;
}

Il secondo esempio di liste collegate è questo:

EnemySpaceShip* addNewEnemyToList (EnemySpaceShip* p_list)
{
    EnemySpaceShip* p_ship = new EnemySpaceShip;
    p_ship->x_coordinate = 0;
    p_ship->y_coordinate = 0;
    p_ship->weapon_power = 20;
    p_ship->p_next_enemy = p_list;
    return p_ship;
}

Quindi il libro scrive questo:

Si noti che questa funzione differisce da getNewEnemy perché restituisce un puntatore all'elenco, piuttosto che il nuovo nemico.

Quello che non capisco è ciò che intende con la "seconda funzione restituisce un puntatore alla lista" e "la prima funzione restituisce il nuovo nemico". Ho pensato che entrambi hanno creato un nuovo nemico chiamato p_ship (che è sia un puntatore che un nuovo nemico) e lo restituisce. Cosa si intende con questa affermazione?


22
2017-09-01 11:13


origine


risposte:


Questa è la linea importante

p_ship->p_next_enemy = p_list;

Si noti che il p_ship ha un puntatore a p_next_enemy che è a sua volta un EnemySpaceShip*. Pertanto, se continuassi a chiamare questa funzione più e più volte, avresti finito con un elenco collegato. Potresti iniziare dal primo EnemySpaceShip* e attraversare tutti loro in un ciclo, ad es.

EnemySpaceShip* p_ship = p_first_ship;    // assume this was known
while (p_ship->p_next_enemy != nullptr)
{
    p_ship = p_ship->p_next_enemy;
    // p_ship has now advanced one element of your linked list
}

Inoltre, a causa dell'ordine con cui vengono aggiunte queste navi, se hai chiamato addNewEnemyToList più volte, proprio l'ultima volta che l'hai chiamato, avresti effettivamente ottenuto un puntatore al primo spedire nell'elenco collegato. Questo è il motivo per cui l'autore dice "restituisce un puntatore alla lista".


20
2017-09-01 11:17



Non penso che la frase abbia un senso.

C'è solo una differenza tra le funzioni. Nella prima funzione l'elenco delle navi è globale rispetto alla funzione. Forse è un membro di dati di una classe e la funzione è una funzione membro della classe che ha accesso ai membri di dati della classe. O in effetti la lista è dichiarata nel namespace globale.

Nella seconda funzione la lista viene passata alla funzione come argomento.

Entrambe le funzioni restituiscono il puntatore ai primi nodi degli elenchi.

Se rimuovere codice non importante dalle funzioni e rendere identici i nomi degli elenchi, si otterrà

EnemySpaceShip* getNewEnemy ()
{
    EnemySpaceShip* p_ship = new EnemySpaceShip;
    //...
    p_ship->p_next_enemy = p_enemies;
    p_enemies = p_ship;
    return p_ship;
}


EnemySpaceShip* addNewEnemyToList (EnemySpaceShip* p_enemies)
{
    EnemySpaceShip* p_ship = new EnemySpaceShip;
    //...
    p_ship->p_next_enemy = p_enemies;
    return p_ship;
}

Come vedi le funzioni differiscono solo in una dichiarazione

p_enemies = p_ship;

è presente nella prima funzione (perché ha accesso alla lista originale stessa) ed è assente nella seconda funzione perché la funzione ha solo una copia della testa della lista (cambiando una copia della testina della lista originale non cambiare la testa originale stessa perché i parametri sono variabili locali delle funzioni).

È possibile chiamare entrambe le funzioni nel seguente modo

p_enemies = getNewEnemy();

p_enemies = addNewEnemyToList( p_enemies );

e come risultato p_enemies sarà la stessa lista a cui è stato aggiunto un nodo.

Solo nella prima funzione la lista cambia anche all'interno della funzione; nella seconda funzione è necessario assegnare il puntatore alla lista perché all'interno della funzione la lista stessa non viene modificata.

Quindi posso concludere che la frase confonde solo i lettori. Dovrebbe essere riscritto in qualche modo per chiarire cosa avrebbe detto l'autore. :) È molto importante nei libri per principianti che tutte le frasi siano chiare.


13
2017-09-01 11:51



Il primo getNewEnemyusa un campo p_enemies, che contiene l'elenco dei nemici. Si aggiunge alla parte anteriore della lista, cambiando p_enemies.

Il secondo addNewEnemyToList utilizza un parametro p_list ma se ne va p_list non modificato (poiché è un parametro di input). Quindi il risultato dovrebbe essere assegnato a qualsiasi ragionevolezza p_list per far crescere quella lista di uno.

Ci si aspetterebbe:

p_enemies = addNewEnemyToList(p_enemies);

Sebbene entrambi siano indicatori di un nuovo nemico, per mantenere la lista, addNewEnemyToList si può dire che restituisca anche la nuova lista.


2
2017-09-01 15:36



Sono d'accordo con Vlad e stavo semplicemente votando la risposta, ma se guardi i due metodi da una prospettiva blackbox, non intendi dove verrà aggiunto il nuovo nemico.

Il nome del primo metodo indica che il nemico appena creato è quello che verrà restituito. Un effetto collaterale è che viene aggiunto a un elenco di p_enemies. Questo nella mia mente sarebbe una violazione del principio di responsabilità singola, ma ciò potrebbe essere alleviato con più contesto intorno a quel metodo. Se il metodo viene aggiornato per aggiungere il nuovo elemento alla fine o in base a un ordine ordinato, non restituirà più il capo dell'elenco.

Il secondo metodo dice esplicitamente che sta aggiungendo un nuovo nemico alla lista passata. Non è chiaro dalla definizione cosa viene restituito e questo potrebbe essere fonte di confusione. Dovrebbe restituire il nuovo nemico o l'elenco aggiornato? Se non restituisce l'elenco, allora il chiamante può essere certo che il nuovo elemento è in testa alla lista? Cosa succede se i requisiti cambiano e il nuovo elemento deve essere aggiunto alla fine dell'elenco? Non è un modo efficace per aggiungere nemici, ma è possibile.

Sembra che il libro stia cercando di indicare che la seconda funzione dovrebbe restituire il capo della lista e non il nuovo nemico. In questo caso, il fatto che il capo della lista sia il nuovo nemico è una coincidenza.


2
2017-09-01 17:22



Si potrebbe anche dire che la funzione getNewEnemy () può essere sostituita da addNewEnemyToList (NULL)

EnemySpaceShip* getNewEnemy ()
{
  p_enemies = addNewEnemyToList( NULL );
  return p_enemies;
}

o addirittura rimosso se si utilizza:

p_enemies = addNewEnemyToList( NULL );
p_enemies = addNewEnemyToList( p_enemies );

0
2017-09-01 14:43



Penso che l'autore voglia dire che la prima funzione è destinata ad assegnare un tipo di puntatore "nemico", la seconda funzione è destinata ad assegnare un tipo di puntatore "elenco di nemici".

Il tipo utilizzato per implementare questi puntatori è lo stesso, ma il loro significato è diverso così come il loro utilizzo nel flusso della logica del programma.

Quindi l'autore dice: attenzione, nel secondo caso si ottiene qualcosa che nel programma si utilizzerà come elenco, non come un elemento della lista ...

Ad esempio, in questo caso specifico la seconda funzione dovrebbe assegnare il p_list parametro alla sua uscita, altrimenti il p_list continua a puntare il precedente oggetto nemico.

Considerate anche che è più probabile che getNewEnemy sia progettato per funzionare con un tipo di dati o un oggetto astratto e che addNewEnemyToList sia progettato per funzionare in uno stile "procedurale".


0
2017-09-01 19:40