为什么我不能为 std::vector 元素取别名?

问题描述 投票:0回答:3

Rust for C++程序员的第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 与 const int&
  2. 从向量改变事物

但我无法弄清楚为什么别名不能基于它们工作。我查看了有关向量定义的http://en.cppreference.com/w/cpp/container/vector,但它似乎只是在磁盘上分配的连续内存。我知道字符串

Hello
world
被分配在程序的数据成员上的某个位置,就像在
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
,程序将正确运行。因此,为什么别名在第二次推送后会丢失对第一个向量元素的引用?

c++ c++11 vector
3个回答
7
投票

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

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


4
投票

当调用方法

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 

3
投票

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

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

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

x
会因初始化之后、输出语句之前
push_back()

的调用而失效。
© www.soinside.com 2019 - 2024. All rights reserved.