我正在编写一个程序,并且有一个很难识别的非常微妙的错误。
我对这个程序进行了几个小时的检查,看起来这是因为我在resize()
中分配新的vector
而误用了vector
,因为我误用了back()
vector
,或更新参考变量。
我写了一个更简单的代码,在我原来的演示程序中包含类似的问题:
int main() {
vector<vector<int>> A(1);
vector<int> &recent = A.back();
recent.emplace_back(50);
cout << A.back()[0] << endl; //prints 50
A.resize(2);
A[1] = vector<int>();
cout << A.size() << endl; //prints 2
recent = A.back();
cout << A[1].size() << endl; //prints 0
recent.emplace_back(20); //Segmentation fault occurs!!
cout << A[1][0] << endl;
}
当我尝试emplace_back(20)
时发生分段错误,虽然在我的原始程序中它不会抛出任何错误,也不会emplace
该元素。
可能的问题原因,在我看来是:
resize()
在2D向量vector
的当前最后位置之后分配新的A
,因为我不知道如何emplace_back()
一个空向量 2,3。在recent = A.back();
中,我不确定我是否正确更新了引用变量(定义为vector<int> &recent
),如果back()
给出正确的引用在2D向量A
的末尾新分配的向量。
逻辑看起来非常好,但显然我做错了。
我做错了什么,我该怎么办呢?
答案 0 :(得分:3)
C ++中的引用无法“更新”。对resize
的调用可能(并且可能会)使对向量的原始内容的任何引用无效。因此recent
是A.resize(2);
之后的悬空参考。
在此处创建初始A
std::vector<std::vector<int>> A(1);
外部向量需要能够存储一个单一的向量。
如果您向std::vector<int>
添加另一个A
,则A
的第一个元素可能会移动到另一个内存位置。由于recent
将始终引用旧的内存位置,因此您会看到段错误。
有关矢量的工作原理,请参阅“c++ Vector, what happens whenever it expands/reallocate on stack?”。
关于如何规避这个问题:
如果您事先知道向量的大小,则可以使用reserve
来阻止向量A
重新分配其内容。然而,您仍然面临无法“重新分配”引用的问题。您始终可以使用A.back()
来引用最后一个元素。
您可以使用一个带参考参数的函数,该参数将在调用函数时绑定:
void do_stuff(std::vector<int> & recent)
{
// do stuff with recent
}
std::vector<std::vector<int>> A;
while (condition)
{
// add whatever to A
A.emplace_back(std::vector<int>{});
// do stuff with last element
do_stuff(A.back());
}
另一种方法是使用scope
:
std::vector<std::vector<int>> A(1);
{
std::vector<int> &recent = A.back();
recent.emplace_back(50);
std::cout << A.back()[0] << std::endl; //prints 50
A.resize(2);
} // recent goes out of scope here
std::cout << A.size() << std::endl; //prints 2
{
std::vector<int> &recent = A.back(); // another recent indepedant of first one
std::cout << A[1].size() << std::endl; //prints 0
recent.emplace_back(20);
std::cout << A[1][0] << std::endl; // prints 20
}
答案 1 :(得分:3)
让我们逐行逐步完成代码。
vector<vector<int>> A(1);
vector<int> &recent = A.back();
在这里,我们创建一个vector
,其中包含一个默认构造的vector<int>
作为其内容。然后,我们将引用绑定到最后一个唯一元素。
recent.emplace_back(50);
cout << A.back()[0] << endl; //prints 50
我们现在将50
放入唯一的矢量并打印出来。
A.resize(2);
现在我们调整vector
的大小。如果需要重新分配空间,所有迭代器,指针和对内容的引用现在都是无效的。
A[1] = vector<int>();
cout << A.size() << endl; //prints 2
这很好,因为A
中有足够的空间。
recent = A.back();
<强> BANG 强>
此作业不重新绑定recent
,它会尝试将A.back()
指定给引用者。如果为A
重新分配空间,recent
不再是有效的引用,那么我们将进入未定义行为的范围。
老实说,直接使用A.back()
而不是保持对它的引用可能是你最好的选择。如果你绝对想要对结尾持有某种引用,那么这是合理使用非拥有指针。
答案 2 :(得分:2)
根据评论中的讨论,您的原始问题似乎是:
<label for="">Click to see differences</label>
<input type="checkbox" class="check-diff">
<div class="compare-diff">
<div class="row">
<div class="col-sm-3 title">Name</div>
<div class="col-sm-3">John</div>
<div class="col-sm-3">Henry</div>
<div class="col-sm-3">Kim</div>
</div>
<div class="row checkDiff">
<div class="col-sm-3 title">Status</div>
<div class="col-sm-3 diff">Single</div>
<div class="col-sm-3 diff">Married</div>
<div class="col-sm-3 diff">Single</div>
</div>
<div class="row checkDiff">
<div class="col-sm-3 title">Car</div>
<div class="col-sm-3 diff">Yes</div>
<div class="col-sm-3 diff">Yes</div>
<div class="col-sm-3 diff">Yes</div>
</div>
<div class="row checkDiff">
<div class="col-sm-3 title">Kids</div>
<div class="col-sm-3 diff">Yes</div>
<div class="col-sm-3 diff">Yes</div>
<div class="col-sm-3 diff">No</div>
</div>
<div class="row checkDiff">
<div class="col-sm-3 title">Home</div>
<div class="col-sm-3 diff">Yes</div>
<div class="col-sm-3 diff">Yes</div>
<div class="col-sm-3 diff">Yes</div>
</div>
</div>
并且你想要一个简写符号来访问它的最后一个元素:
vector<vector<int>> very_long_name_that_cannot_be_changed;
这是防止调整大小的证明,因为引用只跟踪向量,而不是它的最后一个元素。