22

The book I'm reading, Introduction to Data Structures with Linked Lists (Presentation 21), has 2 examples of linked lists. Here is the first one:

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;
}

The second example of linked lists is this one:

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;
}

Then the book writes this:

Notice that this function differs from getNewEnemy because it returns a pointer to the list, rather than the new enemy.

What I don't understand is what he means by the "second function returns a pointer to the list" and "the first function returns the new enemy". I thought that they have both created a new enemy called p_ship (which is both a pointer and a new enemy) and returned it. What is meant by this statement?

Laurel
  • 5,965
  • 14
  • 31
  • 57
  • 1
    Actually you could say that both functions return newly created enemy or that both functions return a list as both functions have the code to maintain the linked list. The only difference is a source of a head of the list. The list implementation is merged with the entity, so it is a mistake to call the result of either function a pure list or an 'enemy' – Serge Sep 01 '16 at 11:24
  • 31
    It looks like an error. Since that is also extremely bad example showing all the _worst_ practices you can use in C++, I would probably recommend looking for more modern C++ book. – Jan Hudec Sep 01 '16 at 11:27
  • 4
    @JanHudec unfortunately there are more worst practices than shown in this example ;) – 463035818_is_not_an_ai Sep 01 '16 at 11:41
  • 17
    Please name the book and author so that this question has search/reuse potential. Improve the title if possible. – Lightness Races in Orbit Sep 01 '16 at 11:47
  • 1
    @JanHudec Such recommendations are best accompanied with a link to the [good book list](http://stackoverflow.com/q/388242/1782465) here on SO. – Angew is no longer proud of SO Sep 01 '16 at 11:52
  • 8
    Please use a title that describes what your issue is. "I didn't understand something" tells the reader nothing about your question: it's obvious that you didn't understand something because, otherwise, there'd be nothing to ask. – David Richerby Sep 01 '16 at 13:29
  • @DavidRicherby the title is just so i attract the correct people to answer the question. all i needed to say is its got to do with c++ so only the people who can code c++ are attracted –  Sep 01 '16 at 14:40
  • 2
    @AmeenIzhac The title is also to attract other people who are interested in the solution to your problem, so that they can read the answers that have already been written, instead of making people spend time writing new answers. – David Richerby Sep 01 '16 at 14:52
  • I would have written "new EnemySpaceShip();" - am I wrong? – TheMathemagician Sep 01 '16 at 15:03
  • 1
    @LightnessRacesinOrbit It seems to be *Introduction to Data Structures with Linked Lists (Presentation 21)*. It's not what *I* would call a book, but it's clearly what OP is using. – Laurel Sep 01 '16 at 18:02
  • @DavidRicherby ok thanks I didn't think of that –  Sep 01 '16 at 19:55

6 Answers6

20

This is the important line

p_ship->p_next_enemy = p_list;

Notice that the p_ship has a pointer to p_next_enemy which is itself a EnemySpaceShip*. Therefore if you kept calling this function over and over, you'd end up with a linked list. You could start at the first EnemySpaceShip* and traverse all of them in a loop, e.g.

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
}

Also, due to the order that these ships are being added, if you called addNewEnemyToList several times, the very last time you called it you'd actually get a pointer to the first ship in the linked list. That is why the author says "it returns a pointer to the list".

Cory Kramer
  • 114,268
  • 16
  • 167
  • 218
  • 8
    All true. It is very odd wording though. The OP is right that what is being returned is a pointer to the new ship. It's just that in the latter case, the ship is part of an intrusive linked list. (And adding to the start wtf) – Lightness Races in Orbit Sep 01 '16 at 11:49
  • @LightnessRacesinOrbit: well, singly-linked list, it's pretty natural to add to the start and use the list as a stack for most purposes. – Steve Jessop Sep 01 '16 at 12:38
  • 4
    The question seems not to be so much about building linked lists as about the book author's remark suggesting that there is something qualitatively different about the return values of the two functions. As @Vlad shows, there isn't. – John Bollinger Sep 01 '16 at 14:15
  • @CoryKramer But he says it returns a pointer to the list rather than the ship but it returns both a pointer and the new ship so why does he say "rather than the new enemy" ? –  Sep 02 '16 at 10:22
  • @AmeenIzhac as others have mentioned I think the authors wording is poor here. They are not returning a pointer to the list *rather* than a pointer to the ship. Instead they are returning a pointer that serves as *both* a pointer to ship *and* a pointer to the linked list, because they are inherently coupled together in the author's example. – Cory Kramer Sep 02 '16 at 11:14
13

I don't think the sentence makes any sense.

There is only one difference between the functions. In the first function the list of ships is global relative to the function. Perhaps it is a data member of a class and the function is a member function of the class that has access to data members of the class. Or indeed the list is declared in the global namespace.

In the second function the list is passed to the function as an argument.

The both functions return pointer to the first nodes of the lists.

If to remove non-important code from the functions and make the names of the lists identical then you will get

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;
}

As you see the functions differ only in one statement

p_enemies = p_ship;

that is present in the first function (because it has access to the original list itself) and is absent in the second function because the function has only a copy of the head of the list (changing a copy of the head of the original list does not change the original head itself because parameters are local variables of functions).

You can call the both functions the following way

p_enemies = getNewEnemy();

p_enemies = addNewEnemyToList( p_enemies );

and as result p_enemies will be the same list to which a node was added.

Only in the first function is the list also changed within the function; in the second function you need to assign the return pointer to the list because within the function the list itself is not changed.

Thus I can conclude that the sentence only confuses readers. It should be rewritten somehow to make clear what the author was going to say. :) It is very important in books for beginners that all sentences be clear.

ruakh
  • 175,680
  • 26
  • 273
  • 307
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 1
    The only way I can interpret the sentence that makes sense to me, is that the first function conceptually returns the new ship, and on the side there's also this global `p_enemies` that it mutates. The second function conceptually pushes a new ship onto the stack of ships provided as input, it mutates nothing, and therefore *necessarily* it returns the new state of the stack. But since, as you point out, "the list" and "the new ship" are exactly the same object, this is quite a fiddly concept to expect someone to grasp from a single sentence. – Steve Jessop Sep 01 '16 at 17:52
  • 1
    So if the goal of the example was to illustrate the difference between the two, then the example is poorly-chosen. Much better would be a list that uses external list nodes. Then the reader would see how the actual pointer returned is different, reflecting the conceptual difference between "a function that returns a new object and as a side-effect mutates a global data structure" and "a function with no side-effects (other than memory allocation) that operates on an immutable data structure and therefore returns the data structure, not the object it has just added to it". – Steve Jessop Sep 01 '16 at 17:56
  • I think that it is conceptually wrong to assign getNewEnemy result to a pointer named p_enemies... instead, it is more likely that getNewEnemy is intended for work with an abstract data type or object, and addNewEnemyToList is intended for work in a "procedural" style... – Ciro Corvino Sep 02 '16 at 09:10
2

The first getNewEnemy uses a field p_enemies, which holds the list of enemies. It adds itself to the front of the list, by changing p_enemies.

The second addNewEnemyToList uses a parameter p_list but it leaves p_list unmodified (as it is an input parameter). Hence the result should in all reasonability be assigned to p_list to let that list grow by one.

One would expect:

p_enemies = addNewEnemyToList(p_enemies);

Though both are pointers to a new enemy, to maintain the list, addNewEnemyToList can be said to return the new list too.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
2

I agree with Vlad and was going to simply up vote that answer, but if you look at the two methods from a blackbox perspective, neither imply where the new enemy will be added.

The name of the first method indicates that the newly created enemy is what will be returned. A side effect is that it is being added to a p_enemies list. This in my mind would be a violation of the single responsibility principle, but that might be alleviated with more context around that method. If the method is updated to add the new item to the end or based on a sorted order then it would no longer be returning the head of the list.

The second method explicitly says it is adding a new enemy to the passed in list. It is not clear from the definition what is returned and this could be confusing. Should it return the new enemy or the updated list? If it doesn't return the list then can the caller be certain that the new item is at the head of the list? What if the requirements change and the new item is to be added to the end of the list? Not an efficient way to add enemies, but it is possible.

It seems that the book may be trying to point out that the second function is supposed to return the head of the list and not the new enemy. In this case, the fact that the head of the list is the new enemy is coincidental.

brader24
  • 485
  • 2
  • 10
0

You could even say that the function getNewEnemy() can be replaced by addNewEnemyToList(NULL)

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

or even removed if you use:

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

I think that the author would mean that the first function is intended to assign an "enemy" pointer type, the second function is intended to assign a "list of enemy" pointer type.

The type used for implementing these pointers it is the same, but the meaning of them it is different as well as their using in the program logic flow.

So the author says: attention, in the second case you are getting something that in the program you are going to use as a list, not as a list item...

For example, in this specific case the second function should have to assign the p_list parameter at his exit, otherwise the p_list continues to pointing the previous enemy object.

Also consider that it is more likely that getNewEnemy is intended for work with an abstract data type or object, and addNewEnemyToList is intended for work in a "procedural" style..

Ciro Corvino
  • 2,038
  • 5
  • 20
  • 33