是否可以从 const 方法迭代链表

Is it possible to iterate over a linked list from a const method

本文关键字:方法 迭代 链表 const 是否      更新时间:2023-10-16

出于我不想讨论的原因,我创建了一个基本的链表,看起来像这样:

class Linked
{
...
std::unique_ptr<Linked> next;
std::string data;
}

我想实现一个将迭代列表的 const 方法,因此:

std::string Linked::getAllData() const
{
std::string allData = data;
const std::string delimiter = " : ";
for( std::unique_ptr<Linked> &i = next; i != nullptr; i = i->next )
allData += delimiter + i->data;
return allData;
}

似乎很简单。但到目前为止,我还没有摆脱编译器对恒常性的抱怨。我想我的问题稍微基本一些。我不想在迭代时修改每个元素,但我也不想使用 const 对象。如果这些老式的c指针,那么使用const Linked *而不是const Linked * const将是一个简单的问题,因为指针是考虑到这个想法的(是它们本身可能不会被重新分配的变量,而不是变量,点到常量数据)。似乎 std::unique_ptr(作为一个普通类)并不完全认同这个概念。

解决这个问题的唯一方法真的是写我的一个const_iterator吗?这对我来说似乎有点丑陋,对于一个非常简单的类和非常简单的方法来说,这很麻烦。简单地迭代常量链表的最佳方法是什么?

不应在遍历函数中使用unique_ptrunique_ptr表示对对象的引用的概念以及该对象的关联所有权。这对于遍历方法来说是不正确的,遍历方法应该只涉及对对象的引用,而所有权保留在链表本身(而不是转移到getAllData函数中的局部变量中)。

只需从带有unique_ptr::get()unique_ptr获取Linked const*,然后从那里继续:

std::string Linked::getAllData() const
{
std::string allData = data; //Initialise 'allData' with 'data' and
//eliminate the unused 'message' variable.
//(I'm guessing this was just a typo in
// your question)
const std::string delimiter = " : ";
//Use 'Linked*' rather than 'unique_ptr', since there is should be no
//ownership management involved in traversing the list.
for (Linked const *i = next.get(); i != nullptr; i = i->next.get()) {
allData += delimiter + i->data;
}
return allData;
}

在处理恒常性问题之前,您还有其他问题需要先解决。您对指针和引用如何工作的基本理解有些偏离目标。即使这里没有const问题,您的 for 循环至少以两种不同的方式完全中断:

for( std::unique_ptr<Linked> &i = next; i != nullptr; i = i->next )

对于初学者,您无法将引用与nullptr进行比较。忽略恒定性问题:i被声明为引用,因此无论是否consti != nullptr都不起作用。只有指针可以比作nullptri不是指针。这是一个参考。

然后我们得到:i=i->next;由于i是一个引用,这将用新值覆盖对象i引用。这显然不是你的意图:用任何next->next来破坏你班next指针。

首先,您根本不需要此处的参考资料。一个简单的指针就可以了,你在这里显然唯一需要做的是使用一个普通的、花园品种的指针:

for( Linked *i = next.get(); i != nullptr; i = i->next.get() )

这完全消除了雷达屏幕上的const问题。它根本不是一个因素。

此外,我还强烈怀疑您打算从this开始迭代,可能:

for( Linked *i = this; i != nullptr; i = i->next.get() )

呃,这是一个丑陋的答案。需要明确的是,我不想使用原始指针进行迭代的全部原因与我一开始不想使用原始指针的原因相同:它们乞求内存泄漏等。通过阅读有关引用的信息,我确实得到了一些灵感,这些引用不能被反弹(指针可以)http://en.cppreference.com/w/cpp/language/reference_initialization

对 T 的引用可以使用 T 类型的对象、T 类型的函数或可隐式转换为 T 的对象进行初始化。初始化后,无法更改引用以引用另一个对象。

意识到这一点,我想到了另一个想法,它允许我绑定引用(无需转移所有权,因为引用是习惯的),保持常量正确性,并防止使用旧式原始指针。但它涉及尾递归。我想这是一种不过早优化的情况?

void appendDataRecursively(std::string &data, const std::unique_ptr<Linked> &node)
{
static const std::string delimiter = " : ";
if(node == nullptr) return;//yes, I can compare it to nullptr
data += delimiter + node->data;
appendDataRecursively(data, node->next);
}
std::string Linked::getAllData() const
{
std::string allData = data;//make a local copy so the copy can be modified
appendDataRecursively(data, next);//append each element in the list
return allData;//Tada!
}