我正在阅读的书 Introduction to Data Structures with Linked Lists (Presentation 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
的新敌人(它既是指针又是新敌人)并将其归还。这句话是什么意思?
答案 0 :(得分:20)
这是重要的一句
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
另外,由于这些船只的添加顺序,如果您多次调用@Bean
public SomeService getSomeService(@Value("${property.key}") String key) throws IOException {
return new SomeService(key);
}
,最后一次调用它时,实际上会得到指向的指针在链表中发货。这就是为什么作者说“它返回一个指向列表的指针”。
答案 1 :(得分:13)
我认为这句话没有任何意义。
这些功能之间只有一个区别。在第一个函数中,船只列表相对于函数是全局的。也许它是类的数据成员,函数是可以访问类的数据成员的类的成员函数。或者实际上列表是在全局命名空间中声明的。
在第二个函数中,列表作为参数传递给函数。
这两个函数返回指向列表的第一个节点的指针。
如果要从函数中删除不重要的代码并使列表的名称相同,那么您将获得
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将与添加节点的列表相同。
仅在第一个函数中,该函数内的列表也发生了变化;在第二个函数中,您需要将返回指针分配给列表,因为在函数中列表本身不会更改。
因此,我可以得出结论,这句话只会让读者感到困惑。它应该以某种方式重写,以明确作者将要说的内容。 :)在初学者的书中,所有句子都清楚是非常重要的。
答案 2 :(得分:2)
第一个getNewEnemy
使用字段p_enemies
,其中包含敌人列表。它通过更改p_enemies
。
第二个addNewEnemyToList
使用参数p_list
,但不会修改p_list
(因为它是输入参数)。因此,结果应该在所有合理性中分配给p_list
,以使该列表增加一个。
人们会期待:
p_enemies = addNewEnemyToList(p_enemies);
虽然两者都是指向新敌人的指针,但为了维护列表,可以说addNewEnemyToList
也会返回新列表。
答案 3 :(得分:2)
我同意弗拉德并且只是简单地对这个答案进行投票,但是如果你从黑盒子的角度来看这两种方法,都不会暗示将添加新的敌人。
第一个方法的名称表示新创建的敌人将被返回。副作用是它被添加到p_enemies列表中。在我看来,这将违反单一责任原则,但可以通过围绕该方法的更多背景来缓解这一点。如果更新方法以将新项目添加到结尾或基于排序顺序,那么它将不再返回列表的头部。
第二种方法明确表示它正在向传入的列表添加新的敌人。从定义中不清楚返回什么,这可能会令人困惑。它应该返回新的敌人还是更新后的名单?如果它没有返回列表,那么调用者可以确定新项目是否在列表的头部?如果需求发生变化并且新项目要添加到列表末尾怎么办?不是添加敌人的有效方式,但它是可能的。
似乎这本书可能试图指出第二个函数应该返回列表的头部而不是新的敌人。在这种情况下,列表的头部是新的敌人的事实是巧合。
答案 4 :(得分:0)
你甚至可以说函数getNewEnemy()可以被addNewEnemyToList(NULL)替换
EnemySpaceShip* getNewEnemy ()
{
p_enemies = addNewEnemyToList( NULL );
return p_enemies;
}
甚至在您使用时删除:
p_enemies = addNewEnemyToList( NULL );
p_enemies = addNewEnemyToList( p_enemies );
答案 5 :(得分:0)
我认为作者意味着第一个功能是为了分配一个敌人"指针类型,第二个功能是分配一个"敌人列表"指针类型。
用于实现这些指针的类型是相同的,但它们的含义以及它们在程序逻辑流程中的使用是不同的。
所以作者说:关注,在第二种情况下,你得到的东西在程序中你将用作列表,而不是作为列表项......
例如,在这种特定情况下,第二个函数必须在其出口处分配p_list
参数,否则p_list
继续指向前一个敌人对象。
还要考虑getNewEnemy更有可能用于处理抽象数据类型或对象,addNewEnemyToList用于在"过程"中工作。风格..