C++使用 .或 -> 表示链表

C++ what is the difference between using . or -> for linked list

本文关键字:表示 链表 gt 使用 C++      更新时间:2023-10-16

假设我有一个结构。

struct node {
 string info;
 node link*;
}

有什么区别

node k;
node b;
k.info = "string";
b.info = "string";
k.link = &b;

node *k;
node *b;
k = new node;
b = new node;    
k->info = "string";
b->info = "string";
k->link = b;

在内存分配方面?这两个例子是否正确,并创建了一个正确的链表添加:在大多数书中使用第二个例子,为什么?使用第一个例子有缺点吗?

是。两者在技术上都是正确的。

在第一个例子中,内存在堆栈上,在第二个例子中在堆上。

当内存在堆栈上时,"编译器"负责释放内存(当变量超出范围时)。由于您的链表可能比您显式创建的变量更长寿,因此这可能会导致各种问题。例如,在第一个示例中,如果b超出范围,但k没有,那么k将有一个悬空指针,这将导致未定义的行为(也称为坏行为)。

当内存在堆上时,必须管理内存,如果忘记释放内存,可能会导致问题。考虑到异常情况,这听起来更难。我建议你使用某种形式的智能指针。这是使用链表最常见、最安全的方法。

两者都是正确的,并将创建一个正确的链表,但第一个示例使用自动分配(全局或堆栈上,在范围内,取决于上下文),第二个示例使用动态分配(对于每个new,您最终必须调用delete)。

何时使用取决于程序的逻辑,但根据我的个人经验,通常链表是使用动态分配(第二个例子)完成的。

它与链表无关。

  • .用于在结构对象已知时引用结构组件
  • ->用于在结构对象的地址已知时引用结构组件

node k, *m, *n;
m = &k;
n = new node;
k.info  = "Hello";   // k is a node type object so use directly . operator
m->info = "Hi";      // m is a pointer to an object to type node, so use -> operator
n->info = "Man";     // n is a pointer to an object to type node, so use -> operator
*(m).info = "This";  // *(m) refers to an object itself, we use . operator on it
*(n).info = "Is a test"; // *(n) refers to an object itself, we use . opeartor on it

所有这些都有有效的语法

  • 在函数内部执行node k;时,它通常在堆栈上分配
  • 当执行static node k;或将node k;声明为全局时,它被分配在可执行文件的.data或类似部分
  • 当您使用new来分配一些内存时,它通常是从堆中分配的

在书中使用了第二个例子,因为您事先不知道会使用多少节点,所以将节点分配为具有大量变量定义的局部变量是不现实的。相反,指针用于临时分配内存,初始化它们,然后将其链接到列表的适当位置。将来,链表中指针链接的地址引用将允许我们按照链接的顺序访问链表的节点。通过这种方式,我们使用了node *k; k = new node;,因此我们有一个类型为node的对象的地址,因此使用k->info;是很自然的,但您可以使用任何语法。您需要记住,->的左手边应该是您想要访问的成员所属类/结构类型的地址,而.运算符的左手边则应该是对象本身。

PS。使用完毕后,应通过delete(使用new分配的)释放内存。

不同之处在于删除节点的位置。

在第一个示例中,它们在从声明的函数返回时自动删除。您不能选择手动删除它们。

在第二个示例中,您需要使用"delete"关键字删除节点。这意味着它们不会被自动删除,但在节点停留的时间方面提供了更大的灵活性。

两者在技术上都是正确的。

差异:首先,内存将在堆栈上分配,您不需要负责释放内存等。编译器将负责所有事情。在第二种情况下,内存将在堆上分配,您需要注意释放内存。

为什么大多数书使用第二种方式:尽管您需要注意内存分配(使用new、malloc()等)和内存释放(delete、free()),但有几个优点:

  1. 很多时候,您需要访问变量范围之外的内存(例如:定义变量的外部函数)。如果不在堆上分配内存,就无法做到这一点。然而,如果你使用指针,你可以很容易地做到这一点。

  2. 您可以使用指针进行复杂而智能的操作。但是,这会使代码难以理解。

我建议你养成第二种方式的习惯,因为它是一种非常强大和聪明的方式(以代码复杂性为代价)。

正如已经指出的,这两种形式的区别在于第二个涉及动态分配,手动管理对象的生存期。由于第二个更复杂可能会问为什么会使用它。原因有两个:第一(已经提到)是对象的生存期对应于由编译器直接管理的生存期。第二就是你事先不知道元素的大小(或数量)。列表的大多数实现将动态分配的原因节点是因为您通常不知道确切的节点数量你需要提前。列表的全部意义在于它是一个动态结构,无固定尺寸。

此https://users.cs.jmu.edu/bernstdh/web/common/lectures/slides_cpp_dynamic-memory.php应清晰地解释动态内存分配

是。这两个例子都是正确的。指向对象的指针可以通过使用->运算符访问结构和类中的函数。指针的内存将在堆上分配。对象使用.运算符来访问函数。这些的内存将在堆栈上。

在处理动态分配的对象时,必须使用->运算符。例如:假设您有一个类List,它有一个返回Nodes的方法getRoot()。节点还具有getNext()方法,它为您提供列表中的下一个节点。

*List myList;

假设我们知道myList是一个有5个节点的列表。列表和5个节点位于堆中(动态分配)。现在,我们要访问最后一个节点。

myList->getRoot()->getNext()->getNext()->getNext()->getRoot();
(*(*(*(*(*myList).geRoot() ).getNext()).getNext()).getNext()).getNext();

这两个句子是等价的,将返回第五个也是最后一个节点。为了程序员的理智,使用第一个。