Подтвердить что ты не робот

Я не понимаю, почему эта функция "возвращает указатель из списка"

Книга, которую я читаю, Введение в структуры данных со связанными списками (Презентация 21), имеет 2 примера связанных списков. Вот первый:

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

Второй пример связанных списков:

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

Затем книга пишет следующее:

Обратите внимание, что эта функция отличается от getNewEnemy, потому что возвращает указатель на список, а не новый враг.

Я не понимаю, что он означает, что "вторая функция возвращает указатель на список" и "первая функция возвращает нового врага". Я думал, что они создали нового врага под названием p_ship (который является и указателем, и новым врагом), и возвратили его. Что подразумевается под этим утверждением?

4b9b3361

Ответ 1

Это важная строка

p_ship->p_next_enemy = p_list;

Обратите внимание, что p_ship имеет указатель на p_next_enemy, который сам является EnemySpaceShip*. Поэтому, если вы продолжаете называть эту функцию снова и снова, вы получите связанный список. Вы можете начать с первого EnemySpaceShip* и пересечь все из них в цикле, например.

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
}

Кроме того, из-за порядка добавления этих кораблей, если вы вызывали addNewEnemyToList несколько раз, в последний раз, когда вы его вызывали, вы действительно получили указатель на первое судно в связанном списке. Вот почему автор говорит, что "он возвращает указатель на список".

Ответ 2

Я не думаю, что предложение имеет смысл.

Существует только одно различие между функциями. В первой функции список кораблей является глобальным относительно функции. Возможно, это член данных класса, и функция является функцией-членом класса, имеющего доступ к членам данных класса. Или, действительно, список объявляется в глобальном пространстве имен.

Во второй функции список передается функции как аргумент.

Обе функции возвращают указатель на первые узлы списков.

Если удалить ненужный код из функций и сделать имена списков одинаковыми, вы получите

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

Как вы видите, функции отличаются только одним утверждением

p_enemies = p_ship;

который присутствует в первой функции (поскольку он имеет доступ к самому исходному списку) и отсутствует во второй функции, потому что функция имеет только копию заголовка списка (изменение копии главы Исходный список не изменяет исходную головку, потому что параметры являются локальными переменными функций).

Вы можете вызвать обе функции следующим образом

p_enemies = getNewEnemy();

p_enemies = addNewEnemyToList( p_enemies );

и в результате p_enemies будет тем же самым списком, в который был добавлен node.

Только в первой функции список также изменяется внутри функции; во второй функции вам нужно назначить обратный указатель на список, потому что внутри функции сам список не изменяется.

Таким образом, я могу заключить, что предложение только путает читателей. Его следует переписать как-то, чтобы четко указать, что автор собирается сказать.:) В книгах для новичков очень важно, чтобы все предложения были ясными.

Ответ 3

В первом getNewEnemy используется поле p_enemies, которое содержит список врагов. Он добавляет себя в начало списка, изменяя p_enemies.

Второй addNewEnemyToList использует параметр p_list, но он оставляет p_list немодифицированным (так как это входной параметр). Следовательно, результат должен быть назначен на p_list, чтобы этот список был увеличен на единицу.

Можно было бы ожидать:

p_enemies = addNewEnemyToList(p_enemies);

Хотя оба указателя на нового врага, для поддержания списка, можно сказать, что addNewEnemyToList также возвращает новый список.

Ответ 4

Я согласен с Владом и собираюсь просто проголосовать за этот ответ, но если вы посмотрите на два метода с точки зрения черного ящика, не подразумевайте, где будет добавлен новый противник.

Имя первого метода указывает, что вновь созданный противник будет тем, что будет возвращено. Побочным эффектом является то, что он добавляется в список p_enemies. Это, на мой взгляд, было бы нарушением принципа единой ответственности, но это могло бы быть смягчено с большим контекстом вокруг этого метода. Если этот метод обновлен, чтобы добавить новый элемент в конец или на основе отсортированного порядка, он больше не будет возвращать головку списка.

Второй метод явно говорит, что он добавляет нового врага в прошедший список. Из определения не ясно, что возвращается, и это может ввести в заблуждение. Должен ли он вернуть нового врага или обновленный список? Если он не возвращает список, может ли вызывающий быть уверенным, что новый элемент находится во главе списка? Что делать, если требования изменяются, и новый элемент должен быть добавлен в конец списка? Неэффективный способ добавления врагов, но это возможно.

Кажется, что книга может пытаться указать, что вторая функция должна вернуть голову списка, а не новый враг. В этом случае совпадение состоит в том, что глава списка является новым врагом.

Ответ 5

Можно даже сказать, что функция getNewEnemy() может быть заменена на addNewEnemyToList (NULL)

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

или даже удалить, если вы используете:

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

Ответ 6

Я думаю, что автор будет означать, что первая функция предназначена для назначения типа "вражеского" указателя, вторая функция предназначена для указания типа указателя "список врагов".

Тип, используемый для реализации этих указателей, один и тот же, но смысл их различен, а также их использование в потоке программной логики.

Итак, автор говорит: внимание, во втором случае вы получаете что-то, что в программе, которую вы собираетесь использовать в качестве списка, а не как элемент списка...

Например, в этом конкретном случае вторая функция должна назначить параметр p_list на своем выходе, в противном случае p_list продолжает указывать предыдущий объект противника.

Также подумайте, что более вероятно, что getNewEnemy предназначен для работы с абстрактным типом данных или объектом, а addNewEnemyToList предназначен для работы в "процедурном" стиле.