1

我正在使用 C++ 中附带的指针向量和迭代器。我最初编写它的方式会导致段错误,但是通过看似微不足道的更改,声明和初始化一个未使用的变量,段错误就会消失。有谁知道为什么?

这是段错误的代码。成功执行的最后一行是第 8 行(通过 printf 语句找到)和取消注释的第 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;
}

如果有人可以澄清为什么会发生这种情况,那将不胜感激!如果我可以添加任何内容来澄清我的问题,请告诉我。

4

3 回答 3

7

vector<Shape *> sceneShapes = scene.getShapes();在堆栈上创建持久对象。 itStartitEnd指向有效内存。在您的第一个示例中,迭代器指向无效内存,因为它们指向来自调用的临时对象,scene.getShapes()该对象已立即被销毁并使您的迭代器无效。

当您取消注释您的//vector<Shape *> sceneShapes = scene.getShapes();行时,它返回的向量是英尺到与临时相同的内存边界,并且迭代器再次有效!但它不是 100% 的机会是相同的,您必须非常小心以避免此类问题。

于 2012-11-16T09:42:07.380 回答
0

这个功能:

scene.getShapes()

可能是按值返回,这意味着当您在第一个示例中调用它时begin()end()您是在尚未绑定的临时对象上调用它们。

如果您无法将函数更改为通过引用返回,则可以将 const 引用绑定到它,然后调用begin()constend()引用,这也将起作用(除了制作您修复的正确副本)。

如果您可以将函数更改为通过引用返回,那就更好了。理想情况下是 const 参考。

注意:返回 const 引用意味着您无法调整向量的大小或更改其中的指针,但指针指向的内容仍然可以更改。这些是指向形状的指针。

于 2012-11-16T10:02:30.147 回答
0

由于已经回答了崩溃的原因,让我尝试回答为什么取消注释您提到的行可能会导致崩溃消失。从函数返回的临时未命名向量将存储在堆栈上,其中将包含指向堆上后备存储的指针(对于在您的情况下是指向 Shape 对象的指针的元素),并且可能很少有人提及大小或结束等等

当迭代器指向后备存储(开始和结束)不再有效时会出现问题,因为临时向量将在语句结束时被销毁(实际上是 2 次)以及内存的后备存储(其中包含指向 Shape 对象的指针)返回给堆管理器。当在堆栈上创建命名向量对象时(当您取消注释已注释掉的行时),向量代码可能会从堆管理器中获取相同的内存块 - 未命名向量得到 - 通过制作迭代器(指针)有效,因此避免了崩溃。在没有命名向量的情况下,堆管理器最终可能会将块分解或将其与大块组合并分配给其他可能随后调用的调用者。

于 2012-11-16T10:20:05.287 回答