我正在使用指针向量和它在C ++中附带的迭代器。我最初写它的方式会导致seg fnault,但是看似微不足道的变化,声明并初始化一个未使用的变量,seg故障消失了。有谁知道为什么?
这是代码出错的代码。成功执行的最后一行是第8行(通过printf语句找到),第4行是unfmeting第4行获取的段错误:
1 Intersect RayTracer::closestShape(Ray r){
2 vector<Shape *>::iterator itStart = scene.getShapes().begin();
3 vector<Shape *>::iterator itEnd = scene.getShapes().end();
4 //vector<Shape *> sceneShapes = scene.getShapes(); This is the unused line that will cause the code to run successfully if I uncomment it.
5 Intersect closest = Intersect();
6 for(;itStart != itEnd; itStart++){
7 Intersect currentIntersect = (*itStart)->intersect(r);
8 if(currentIntersect.isHit()){
9 if(currentIntersect.getT() < closest.getT()){
10 closest = currentIntersect;
}
}
}
return closest;
}
这是不再出现段错误的工作版本:
1 Intersect RayTracer::closestShape(Ray r){
2 vector<Shape *> sceneShapes = scene.getShapes();
3 vector<Shape *>::iterator itStart = sceneShapes.begin();
4 vector<Shape *>::iterator itEnd = sceneShapes.end();
5 Intersect closest = Intersect();
6 for(;itStart != itEnd; itStart++){
7 Intersect currentIntersect = (*itStart)->intersect(r);
8 if(currentIntersect.isHit()){
9 if(currentIntersect.getT() < closest.getT()){
10 closest = currentIntersect;
}
}
}
return closest;
}
如果有人能够澄清为何会发生这种情况,那将非常感谢!如果我可以添加任何内容来澄清我的问题,请告诉我。
答案 0 :(得分:7)
vector<Shape *> sceneShapes = scene.getShapes();
在堆栈上创建持久对象。
itStart
和itEnd
指向有效内存。在您的第一个示例中,迭代器指向无效的内存,因为它们指向来自调用scene.getShapes()
的临时对象,该对象已被立即销毁并使您的迭代器无效。
当您取消注释//vector<Shape *> sceneShapes = scene.getShapes();
行时,它会返回与临时相同的内存边界的向量,并且迭代器再次有效!但它不是百分之百的可能性,你必须非常小心避免这些问题。
答案 1 :(得分:0)
此功能:
scene.getShapes()
可能按值返回,这意味着当您在第一个示例中调用begin()
和end()
时,您将在未绑定的临时字符上调用它们。
如果您无法通过引用更改函数,则可以将const引用绑定到它,然后在const引用上调用begin()
和end()
,这也将起作用(除了使正确的副本,这是你的修复)。
如果您可以将功能更改为按引用返回,那就更好了。理想情况下是const引用。
注意:返回const引用意味着您无法调整向量的大小或更改其中的指针,但指针指向的内容仍然可以更改。这些是指向Shape的指针。
答案 2 :(得分:0)
因为崩溃的原因已经得到解答,让我试着回答为什么不注释你提到的那条线可能会导致崩溃消失。从函数返回的临时未命名向量将存储在堆栈中,该堆栈将包含指向堆栈的指针(对于在您的情况下是指向Shape对象的元素),并且可能很少有其他人提及大小或结束等。
当迭代器指向后备存储(开始和结束)不再有效时会发生问题,因为临时向量将在语句结束时被破坏(确实是2次),并且随之而来的是后备存储memory(包含指向Shape对象的指针)返回给堆管理器。当在堆栈上创建一个命名的向量对象时(当你取消注释该注释掉的行时),向量代码可能会从堆管理器获得相同的内存块 - 未命名的向量 - 并通过制作迭代器(指针)有效,从而避免崩溃。在没有命名向量的情况下,堆管理器可能最终将块分解为碎片或将其与大块组合并将其分配给可能随后调用的其他呼叫者。