Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/151.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 有没有什么优雅的方法可以遍历元素为';立场可以改变吗?_C++_List - Fatal编程技术网

C++ 有没有什么优雅的方法可以遍历元素为';立场可以改变吗?

C++ 有没有什么优雅的方法可以遍历元素为';立场可以改变吗?,c++,list,C++,List,我现在遇到了一个令人讨厌的问题。假设有一个对象列表(我们称之为对象的类型),我想遍历它。基本上,代码是这样的: for(int i = 0; i < aList.Size(); ++i) { aList[i].DoSth(); } Object::DoSthBig() { Object* pNext = GetNext(); if(pNext) pNext->DoSthBig(); DoSth(); } for(int i=0;i

我现在遇到了一个令人讨厌的问题。假设有一个对象列表(我们称之为对象的类型),我想遍历它。基本上,代码是这样的:

for(int i = 0; i < aList.Size(); ++i)
{
    aList[i].DoSth();
}
Object::DoSthBig()
{
    Object* pNext = GetNext();
    if(pNext)
        pNext->DoSthBig();

    DoSth();
}
for(int i=0;i
这里最困难的部分是,DoSth()方法可能会改变调用方在列表中的位置!因此,可能会出现两种后果:第一,迭代可能永远无法结束;其次,可能会跳过一些元素(迭代不一定像上面那样,因为它可能是一个链表)。当然,第一个问题是主要问题

必须通过以下约束条件解决问题:

1) 不能排除进行换位操作的可能性

2) 如果必要且可行,位置交换操作可以延迟到迭代完成

3) 由于迭代经常发生,因此只能对迭代进行最小程度的修改(因此不建议执行创建列表副本之类的操作)

<>我使用的语言是C++,但是我认为java和C语言中也有类似的问题。
以下是我尝试过的:

a) 尝试在迭代期间禁止位置交换操作。然而,这涉及到太多的客户机代码文件,查找和修改所有这些文件是不实际的

b) 修改对象的每一个方法(例如,方法()),这些方法可以改变自身的位置,并且将被DoSth()直接或间接地调用:首先,我们可以知道列表正在进行迭代,我们将相应地处理方法()。如果迭代正在进行中,那么我们会延迟Method()想要做的事情;否则,它现在就做它想做的事。这里的问题是:在这里延迟函数调用的最佳方式(易于使用,但足够有效)是什么?方法()的参数可能相当复杂。此外,这种方法还将涉及到相当多的功能

c) 尝试修改迭代过程。我在这里遇到的实际情况非常复杂,因为它涉及两层迭代:第一层是普通数组迭代,第二层是递归函数中的典型链表迭代。目前,对于第二层迭代,我能做的最好的事情是限制其迭代时间,并防止同一元素被多次迭代


所以我想有更好的方法来解决这个问题?也许一些很棒的数据结构会有所帮助?

您的问题在细节上有点轻描淡写,但从您所写的内容来看,您似乎犯了混合关注点的错误

您的对象可能会执行某些操作,导致其继续存在或不存在。它不应该再存在的决定与将其实际存储在容器中的决定是一个单独的问题

因此,让我们将这些担忧分开:

#include <vector>

enum class ActionResult {
    Dies,
    Lives,
};

struct Object
{
    ActionResult performAction();
};

using Container = std::vector<Object>;

void actions(Container& cont)
{
    for (auto first = begin(cont), last = end(cont)
        ; first != last
        ; )
    {
        auto result = first->performAction();
        switch(result)
        {
            case ActionResult::Dies:
                first = cont.erase(first);  // object wants to die so remove it
                break;

            case ActionResult::Lives:       // object wants to live to continue
                ++first;
                break;
        }
    }
}
#包括
枚举类ActionResult{
死亡,
生活,
};
结构对象
{
ActionResult性能();
};
使用Container=std::vector;
无效操作(容器和容器)
{
对于(自动第一个=开始(续),最后一个=结束(续)
第一个
; )
{
自动结果=第一->性能();
开关(结果)
{
案例操作结果::死亡:
first=cont.erase(first);//对象想要消亡,所以将其移除
打破
case ActionResult::Lives://对象希望继续生存
++第一,;
打破
}
}
}
如果操作的结果只有两个,即生存和死亡,那么我们可以用惯用的方式来表达这个迭代:

#include <algorithm>

// ...

void actions(Container& cont)
{
    auto actionResultsInDeath = [](Object& o)
    {
        auto result = o.performAction();
        return result == ActionResult::Dies;
    }; 

    cont.erase(remove_if(begin(cont), end(cont), 
                         actionResultsInDeath),
               end(cont));
}
#包括
// ...
无效操作(容器和容器)
{
自动操作结果死亡=[](对象和o)
{
自动结果=o.性能();
返回结果==ActionResult::死亡;
}; 
继续擦除(如果开始(继续),结束(继续),则删除,
诉讼结果(死亡),
完(续);;
}

好吧,问题解决了,至少就我现在感兴趣的情况而言。在我的情况下,aList实际上是一个链表,Object元素是通过指针访问的。如果aList的大小相对较小,则我们有一个优雅的解决方案,如下所示:

for(int i = 0; i < aList.Size(); ++i)
{
    aList[i].DoSth();
}
Object::DoSthBig()
{
    Object* pNext = GetNext();
    if(pNext)
        pNext->DoSthBig();

    DoSth();
}
这有一个基本假设,即在这个过程中,每个pNext都保持有效。但是,如果元素删除操作已经被谨慎地处理,那么一切都很好


当然,这是一个非常特殊的示例,无法应用于其他情况。

在迭代列表时修改列表是体验奇怪行为甚至异常行为的好方法。这可能是因为设计问题或实现设计的问题。使用索引列表存储位置并在循环后应用位置交换。作为一种可能的(可能是临时的)解决方案,您可以创建一个新容器,并在迭代第一个原始容器时插入其中。完成后再从新的复制到原来的。或者,您可以将迭代更改为不使用迭代器而使用位置,这在使用“默认”容器
vector
时非常容易。是否可以对顺序进行任何修改,或者是否可以保证它们的某些方面(例如,始终向后,始终向前)?可以修改
DoSth
以返回新位置吗?@Angew DoSth()只是一个简单的函数,可以被客户端代码重写以执行任何他们想要的操作。它没有任何返回值,也不应该在意。至于立场交换问题,实际上