使用C++中的Templates实现链表
Linked list implementation using Templates in C++
我是C++的新手,最近一直在研究数据结构。我以以下方式创建了链接列表:
class Random{
private:
struct Node{
int data;
Node* next;
};
}
但我遇到了一段代码,它以以下方式做同样的事情:
template<Typename T>
struct Listnode {
T data;
shared_ptr<ListNode<T>> next;
};
我查了一下,发现当我们想要有多种数据类型时,我们会使用模板。像现在一样,我们可以使用int
、double
、float
而不是"T"。而在前一种情况下,我们只能使用int
。然而,我不明白如何:
Node* next
与相同
shared_ptr<ListNode<T>> next
以及这些将如何命名,我知道对于前者,我们使用:
Node->next = new Node;
Node->data = randomdata;
前一种方式是如何运作的。这两个实现的另一个问题是,哪一个更好,为什么?
T*ptr;form是声明指向内存的指针的基本方法,该内存持有类型为T的值。这种指针由数组T[]的基地址、new T()、new T[]或其他方法初始化。
正如你现在所看到的,有很多方法可以分配指针所指向的内存。这是释放所用内存的陷阱之一。您应该使用delete、delete[],还是我们指向的内存甚至不是我们分配的?如果我们忘记释放已分配的内存,或者试图访问已释放的内存,该怎么办?
=>使用原始指针,错误很容易发生!
聪明的人来拯救我们!智能指针,如std::unique_ptr,std::sharedptr为我们封装了这些原始指针,并处理类型安全的内存管理。因此,当超出范围时,unique_ptr中的内存会自动释放。如果不存在对shared_ptr的引用,那么它也适用。
我总是建议尽可能使用c++的智能指针!您应该使用哪种类型的智能指针取决于您想要实现的链表类型(例如,如果也支持循环列表)。顺便说一句,你有没有想过std::vector或std::list?
第二种形式是一种"智能"指针。大多数使用现代c++的代码都应该使用它们。
使用原始(非智能)指针,当对象超出范围时,您必须记住进行new/delete或new[]/delete[]的配对。在构造函数/析构函数的简单情况下,这并不是一个太大的负担。但是,当您在函数中使用指针,而该函数抛出异常时,释放指针会变得有点棘手。
智能指针有多种类型。独特的、共同的和脆弱的。唯一性是指只在一个地方使用的一次性对象(如对象或函数)。Shared适用于多个对象使用同一指针/资源的情况,并且您只想在指针的最后一个所有者超出范围时调用所分配对象的析构函数。弱点是指资源由其他人管理,并且在具有弱指针的对象超出范围后,指向的资源应该继续存在的情况(它们也是避免防止GC和导致内存泄漏的循环分配所必需的)。
聪明的指针是一件好事,你应该好好读一读(Stroustrups的书很棒)。现在很少有需要裸指针的情况。
正如Karoly Horvath所说,这不是一回事:
T*
是指向类型为T
的对象的"普通"指针,它在内存中存储一个地址,并隐式地存储我们可以在该地址找到的对象的类型(这对于知道目标内存的大小等很有用)std::shared_ptr<T>
是一个属于"智能指针"类别的对象,被称为"智能",因为它们可以通过跟踪对该内存位置的引用数量来管理被指向的内存。这意味着在实际操作中,当代码在运行时不再使用动态分配的内存时,它将为您释放
我想说的是,对于一个简单的链表(单链表或双链表),不需要使用shared_ptr
s。它可能很有用,例如,对于具有动态递归结构的图。不过,为了通用性起见,最好使用节点的模板版本:
template <typename T>
struct ListNode
{
T data;
ListNode<T> *next;
};
首先让我们清除一些垃圾:
- 学习和理解链表的原始实现是很好的,因为您将在生产代码中遇到它们(出于许多好的或坏的原因)
- 如果你不得不在代码中使用"侵入性"链表,模板和"智能指针"会让你省去麻烦。(IMO)
- 集合类/模板几乎总能为您提供更好的服务
有了以上注意事项:
std::shared_ptr
是一个"智能指针",它封装了一个原始指针(通常通过调用operator new
产生),并将RAII风格的语义与一个允许底层对象的多个持有者引用内容而不会使其消失的契约一起添加到混合中。(正确使用时。)
链表"只是"程序在不移动数据的情况下遵循(希望但不要求)同构类型的惯例。使用("旧学校",而不是坏)链接列表所有链接管理都是您的责任。要么忘记,要么分心并"忘记"释放资源是非常容易的。这是导致许多夜晚调试和令人讨厌的事情(称为"内存泄漏")的原因。
混合"模板链接列表"的"更好"之处在于减少了资源管理责任。它并没有被消除。它将有助于减少忘记删除已分配节点的内存泄漏类型。它不会消除由循环引用引起的"内存泄漏"。(这种情况下,需要"外部参与者"来打破循环链,并且在"真实"代码中可能非常复杂。)
std::shared_ptr
定义了运算符,允许您"假装"与std::shared
指针正在管理的内容进行交互。这样,代码在视觉上看起来基本相同,但类(/可能)代表您隐藏了一点复杂性。(指针检查等)
哪个更好?国际海事组织也没有。然而,如果只在这两者之间进行选择,在大多数情况下,我绝对更喜欢"更智能的指针"版本,而不是"自己动手"风格。
如果是我,我会选择另一个集装箱。然而,如果您的意图是了解如何实现这些容器的基本原理(这是一件好事!),这并不是您想要听到的答案。:-)
- 实现链表数组
- 如何实现链表的 end()?
- 在 c++ 中实现链表时出现分段错误
- 我们可以在不使用head指针的情况下通过使用head的简单变量而不是head的指针来实现链表吗
- 如何实现链表并允许用户选择要使用 C++ 删除的特定节点?
- 学习在C++中实现链表堆栈类
- 学习在C++中实现链表队列
- 如何在不使用指针的情况下实现链表
- 实现链表时的等号
- 迭代器实现 -(链表队列)
- 实现链表 [C++] 编译错误
- 我们可以使用继承实现链表吗?
- 在C++中实现链表时的运行时(或逻辑)错误
- 正确实现链表的复制构造函数
- C++指针返回结构实现:链表
- 难以在C++中实现链表
- 使用C++中的Templates实现链表
- c++ - OOP实现链表,我不知道为什么添加到末尾不适合我,请建议
- C++ 使用智能指针实现链表 - 'head'未在此范围内声明
- 实现链表的选择排序