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

时间:2017-03-20 01:10:42

标签: c++ linked-list unique-ptr const-correctness

由于我不会进入的原因,我创建了一个看起来像这样的基本链表:

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

看起来很简单。但到目前为止,我还没有超越编译器对constness的抱怨。我想我的问题稍微有点根本。我不想在迭代时修改每个元素,但我也不想要const对象。如果是这些老派的c指针,那么使用const Linked *代替const Linked * const就是一件简单的事情,因为指针考虑了这个想法(变量本身可能不会被重新分配)与变量相比,指向const数据)。似乎std :: unique_ptr(是一个普通的类)并没有完全分享这个概念。

唯一可以解决这个问题的方法是写一个const_iterator吗?这对我来说似乎有点难看,而且对于一个非常简单的类和非常简单的方法来说有很多不妥。简单地迭代const链表的最佳方法是什么?

3 个答案:

答案 0 :(得分:1)

在处理常量问题之前,首先要解决其他问题。您对指针和引用如何工作的基本理解有点不合适。即使这里没有const问题,你的for循环也至少会以两种不同的方式完全打破:

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

对于初学者,您无法比较对nullptr的引用。忽略常量问题:i被声明为引用,因此i != nullptr无效,const。只能将指针与nullptr进行比较,而i不是指针。它是一个参考。

然后我们开始: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() )

答案 1 :(得分:1)

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

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

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

答案 2 :(得分:0)

呃,这是一个丑陋的答案。要明确的是,我不想使用原始指针进行迭代的全部原因与我不希望开始的原始指针的原因相同:它们是在寻求内存泄漏等等。我确实得到了一些灵​​感来自阅读有关无法反弹的参考文献(指针的方式)http://en.cppreference.com/w/cpp/language/reference_initialization

  

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

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

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