【问题标题】:how to use vector<T>::reverse_iterator with one element如何将 vector<T>::reverse_iterator 与一个元素一起使用
【发布时间】:2017-03-27 15:32:58
【问题描述】:

我将 poll() 与 std::vector 一起使用。 注册监听套接字。

std::vector<struct pollfd> fds;
fds.push_back(server_sock);

并添加新的客户端套接字或连接的客户端会话。

// poll() ...
for(std::vector<struct pollfd>::reverse_iterator it = fds.rbegin(); it != fds.rend(); it++) {
    if (it->fd == server_sock) {
        struct pollfd newFd;
        newFd.fd = newClient;
        newFd.events = POLLIN;
        fds.push_back(newFd);
    } else {
        // do something.
    }
}

但是当向量元素为 1 或 2 或 4 时,reverse_iterator 无法正常工作。我不明白为什么会这样。

附上示例代码。

typedef struct tt_a {
    int a;
    short b;
    short c;
} t_a;

vector<t_a> vec;
for (int i = 0; i < 1; i++) {
    t_a t;
    t.a = i;
    t.b = i;
    t.c = i;
    vec.push_back(t);
}

for(vector<t_a>::reverse_iterator it = vec.rbegin(); it != vec.rend(); it++) {
    if (it->a == 0) {
        t_a t;
        t.a = 13;
        t.b = 13;
        t.c = 13;
        vec.push_back(t);
    }

    printf("[&(*it):0x%08X][it->a:%d][&(*vec.rend()):0x%08X]\n",
            &(*it), it->a, &(*vec.rend()));
}

printf("---------------------------------------------\n");

for(vector<t_a>::reverse_iterator it = vec.rbegin(); it != vec.rend(); ++it) {
    if (it->a == 3) {
        it->a = 33;
        it->b = 33;
        it->c = 33;
    }
    printf("[&(*it):0x%08X][it->a:%d][&(*vec.rend()):0x%08X]\n",
            &(*it), it->a, &(*vec.rend()));
}

结果:

[&(*it):0x01ADC010][it->a:0][&(*vec.rend()):0x01ADC028]
[&(*it):0x01ADC008][it->a:33][&(*vec.rend()):0x01ADC028]
[&(*it):0x01ADC000][it->a:0][&(*vec.rend()):0x01ADC048]

如果vector有5个元素,则正常工作。

[&(*it):0x007620A0][it->a:4][&(*vec.rend()):0x00762078]
[&(*it):0x00762098][it->a:3][&(*vec.rend()):0x00762078]
[&(*it):0x00762090][it->a:2][&(*vec.rend()):0x00762078]
[&(*it):0x00762088][it->a:1][&(*vec.rend()):0x00762078]
[&(*it):0x00762080][it->a:0][&(*vec.rend()):0x00762078]
---------------------------------------------
[&(*it):0x007620A8][it->a:13][&(*vec.rend()):0x00762078]
[&(*it):0x007620A0][it->a:4][&(*vec.rend()):0x00762078]
[&(*it):0x00762098][it->a:33][&(*vec.rend()):0x00762078]
[&(*it):0x00762090][it->a:2][&(*vec.rend()):0x00762078]
[&(*it):0x00762088][it->a:1][&(*vec.rend()):0x00762078]
[&(*it):0x00762080][it->a:0][&(*vec.rend()):0x00762078]

【问题讨论】:

  • 顺便说一句,回答您关于为什么这适用于 1、2、4 向量大小的问题是由于向量如何重新分配自身。我假设您所说的 1,2,4 向量大小是您准备向量的第一个 for 循环。向量增长为 size_needed + size_needed/2 对于 1、2、4 它需要通过使先前的分配无效并且您的向量变得无效来重新分配那里。对于 5,当时的向量容量为 6 (4 + 4/2),有足够的空间,分配的项目不会移动,所以它会起作用。

标签: c++ vector reverse-iterator


【解决方案1】:

push_back invalidates iterators 当它导致size 超出容量时:

如果新的 size() 大于 capacity() 则所有迭代器和引用(包括过去的迭代器)都将失效。否则只有过去的迭代器无效。

基本上,如果您必须push_back,请确保提前reserve,以免您的迭代器无效。

【讨论】:

  • 不仅如此。反向迭代器比正向迭代器更容易失效,即使在获得反向迭代器之前调用了vector::reserve,上面的代码仍然会使迭代器失效。
  • 谢谢,ShadowRanger。我修复了我的代码。完成 push_back 之后,我重新分配了迭代器值。 vec.push_back(t); it = vec.rbegin() + index + i;
【解决方案2】:

您的程序很可能已经崩溃。您正在操作容器,同时仍在对其进行迭代。

[&(*it):0x01ADC008][it->a:33][&(*vec.rend()):0x01ADC028]

您可以看到垃圾“33”,而它应该是“13”。

你为什么还要尝试取消引用结束迭代器

&(*vec.rend())

无论矢量大小如何,这基本上都是垃圾。这是一种未定义的行为,会随机使您的应用程序崩溃。

正如阴影指出在迭代之前修复向量大小,但我仍然不确定这将如何修复您的代码,因为您的示例还有其他会导致 seg 错误的问题

【讨论】:

    【解决方案3】:

    对于普通(正向,而不是反向)向量迭代器,插入向量中会使指向插入点处或之后的任何位置的任何迭代器失效。此外,如果必须调整向量的大小,则所有迭代器都将失效。

    仅此一项就可以解释您的问题,因为您没有在向量中保留空间(通过调用vec.reserve(SIZE)),您的任何push_back 调用都可能触发调整大小并使您的迭代器无效,这将导致未定义的行为当您之后尝试使用它们时。

    然而,反向迭代器更复杂,同样的保证不适用于反向迭代器,我相信任何插入都可能使它们失效。

    在内部,反向迭代器在它所指向的元素之后保存一个正向迭代器。取消引用时,反向迭代器递减此正向迭代器并返回其取消引用的值。所以rbegin() 内部有一个end() 的副本,而rend() 有一个begin() 的副本。上述正向迭代器失效规则意味着,至少,如果在反向迭代器位置之后直到一个元素的任何点发生插入,反向迭代器将失效。因此,如果您有一个迭代器指向长度为 1 的向量中的索引 0,push_back 将插入索引 1,这将使迭代器无效。如果您随后继续使用该迭代器(例如在随后的 printf 调用中取消引用它时),那么您将有未定义的行为。

    未定义的行为意味着任何事情都可能发生,而且通常不同的系统会产生不同的行为。 不要仅仅因为此代码在初始向量大小为 5 的系统上按预期运行,就认为它可以在其他系统上运行。任何调用未定义行为的代码本质上都是脆弱的,应该避免。

    对我来说(运行 Visual Studio 2015),无论向量的大小如何,我都会在 printf 行发生崩溃。如果我调用vec.reserve(10) 来消除调整大小无效问题,那么它只会在vec 最初的长度为1 时崩溃。

    此外,您在 printf 参数中取消引用 vec.rend(),这也是未定义的行为,即使您只是想从中获取地址。 (我必须注释掉这个才能让你的代码运行,否则即使没有push_back 调用,它每次都会崩溃。)

    【讨论】:

    • 在我的系统上(使用 Visual Studio 2015,调试版本),此时我得到一个调试断言失败,给出错误“表达式:向量迭代器不可递减”。我怀疑&amp;(*vec.rend())等价于&amp;(*(vec.begin()-1)),而vec.begin()-1是非法的。
    • 不,vec.begin()-1 只有在您取消引用 vec.rend() 时才会发生。而it != vec.rend() 只会将it 的内部前向迭代器与vec.rend() 的内部前向迭代器进行比较,而无需递减任何一个内部前向迭代器。
    • 反正没关系,因为是未定义的行为,因为不满足迭代器operator*的前置条件。与取消引用不存在的对象无关。我猜你用“取消引用”来表示“应用*运算符”但*运算符首先是一个函数调用,只有满足前提条件才能谈论函数的副作用,例如实际执行取消引用.
    • 正确,我使用“取消引用”来表示调用reverse_iterator::operator*()。所以更正确地说,如果 reverse_iterator 等于 rend(),则调用 reverse_iterator::operator*() 是非法的。
    猜你喜欢
    • 2013-02-18
    • 1970-01-01
    • 1970-01-01
    • 2011-07-19
    • 2022-11-24
    • 2013-02-08
    • 2012-05-02
    • 2020-08-26
    相关资源
    最近更新 更多