【问题标题】:Why I cannot take a alias to a std::vector element?为什么我不能为 std::vector 元素取别名?
【发布时间】:2023-04-05 16:11:01
【问题描述】:

Rust for C++ programmers 的幻灯片 6 上,有以下代码:

#include <iostream>
#include <vector>
#include <string>

using namespace std;

int main()
{
    vector<string> v;
    v.push_back("Hello");

    string& x = v[0];
    v.push_back("world");

    cout << x << endl;
    return 0;
}

运行它我得到了:

g++ --std=c++11 main.cpp -I . -o main
./main
P▒▒o▒Y ▒▒2.▒8/.▒H/.▒H/.▒X/.▒X/.▒h/.▒h/.▒x/.▒x/.▒▒/.
@▒▒
...

而且它会继续提供更多的东西。我发现了一些关于别名和向量的问题:

  1. int vs const int&
  2. Changing things from a vector

但我无法根据它们弄清楚为什么别名不起作用。我查看了关于向量定义的http://en.cppreference.com/w/cpp/container/vector,但它似乎只是在磁盘上分配了继续内存。我知道字符串Helloworld 分配在程序数据成员的某个位置,就像g++ main.cpp -S 的程序集一样:

...
.lcomm _ZStL8__ioinit,1,1
    .def    __main; .scl    2;  .type   32; .endef
    .section .rdata,"dr"
.LC0:
    .ascii "Hello\0"
.LC1:
    .ascii "world\0"
    .text
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main
main:
...

如果我不推送第二个元素world,程序会正确运行。因此,为什么别名在第二次推送后丢失了对第一个向量元素的引用?

【问题讨论】:

  • 等等,你正在阅读的幻灯片解释了它。
  • 对不起,这是push_back 文档中的第一句话。 If the new size() is greater than capacity() then all iterators and references (including the past-the-end iterator) are invalidated. 真正的问题是他们为什么要这样做。
  • 如果不这样做,你将如何实现一个为所有元素都拥有一块连续内存的数据结构?你可以想象一些技巧,但到那时,你首先会失去从向量中获得的收益。
  • 是的,这就是问题所在。通常你不能这样做,因为这是分配内存的方式。例如,如果我在向量的末尾添加一个新元素并且我需要增加它的大小,我需要幸运的是,在向量的末尾有空闲内存。我敢打赌,情况绝不会如此,因此需要在一个足以容纳整个结构的洞内的其他地方分配一个完整/全新的内存块。此外,根据动态数组的当前大小,分配更多空间的操作成本非常高 O(n)。

标签: c++ c++11 vector


【解决方案1】:

当调用push_back 方法时,向量可以重新分配已使用的内存,结果引用变得无效。

您可以在向向量添加新元素之前保留足够的内存。在这种情况下,引用将是有效的。例如

vector<string> v;
v.reserve( 2 );

v.push_back("Hello");

string& x = v[0];
v.push_back("world");

这是一个演示程序

#include <iostream>
#include <vector>
#include <string>

int main() 
{
    std::vector<std::string> v;
    v.reserve( 2 );

    v.push_back("Hello");

    std::string& x = v[0];
    v.push_back("world");

    std::cout << x << ' ' << v[1];
    std::cout << std::endl;

    return 0;
}

它的输出是

Hello world 

【讨论】:

    【解决方案2】:

    当您执行第二次 push_back 迭代器和引用时,应假定它们已失效。该向量可能会调整其数据块的大小 - 最有可能在另一个内存位置。

    因此,变量引用 x 正在引用未分配的内存,这随后会导致未定义的行为。

    【讨论】:

      【解决方案3】:

      push_back() 调整向量的大小(这是向其添加元素的固有方法)。

      这会使引用该向量元素的所有迭代器、指针和引用无效。

      通过无效的迭代器、指针或引用(即在调整大小操作之前有效,但之后无效)访问向量的元素会产生未定义的行为。

      x 在其初始化之后和输出语句之前发生的push_back() 调用无效

      【讨论】:

        猜你喜欢
        • 2011-06-18
        • 1970-01-01
        • 1970-01-01
        • 2022-01-20
        • 2022-03-27
        • 2022-06-10
        • 1970-01-01
        • 1970-01-01
        • 2015-09-07
        相关资源
        最近更新 更多