导出类对象丢失vfptr的指针

时间:2010-04-25 10:02:16

标签: c++ virtual-functions raytracing

首先,我正在尝试编写一个普通的,简单的Ray Tracer。在我的Ray Tracer中,我在世界上有多种类型的几何,都来自一个名为“SceneObject”的基类。我在这里已经包含了标题。

/**
Interface for all objects that will appear in a scene
*/
class SceneObject
{
public:
 mat4 M, M_inv;
 Color c;

 SceneObject();
 ~SceneObject();

 /**
 The transformation matrix to be applied to all points
 of this object.  Identity leaves the object in world frame.
 */
 void setMatrix(mat4 M);
 void setMatrix(MatrixStack mStack);
 void getMatrix(mat4& M);

 /**
 The color of the object
 */
 void setColor(Color c);
 void getColor(Color& c);

 /**
 Alter one portion of the color, leaving
 the rest as they were.
 */
 void setDiffuse(vec3 rgb);
 void setSpecular(vec3 rgb);
 void setEmission(vec3 rgb);
 void setAmbient(vec3 rgb);
 void setShininess(double s);

 /** 
 Fills 'inter' with information regarding an intersection between
 this object and 'ray'.  Ray should be in world frame.
 */
 virtual void intersect(Intersection& inter, Ray ray) = 0;

 /**
 Returns a copy of this SceneObject
 */
 virtual SceneObject* clone()  = 0;

 /**
 Print information regarding this SceneObject for debugging
 */
 virtual void print() = 0;
};

正如您所看到的,我已经包含了一些要在其他地方实现的虚拟功能。在这种情况下,我只有两个派生类 - Sphere和Triangle,它们都实现了缺少的成员函数。最后,我有一个Parser类,它充满了实际的“光线跟踪”部分的静态方法。以下是相关部分的几个片段

void Parser::trace(Camera cam, Scene scene, string outputFile, int maxDepth) {
 int width = cam.getNumXPixels();
 int height = cam.getNumYPixels();
 vector<vector<vec3>> colors;
 colors.clear();
 for (int i = 0; i< width; i++) {
  vector<vec3> ys;
  for (int j = 0; j<height; j++) {
   Intersection intrsct; 
   Ray ray; cam.getRay(ray, i, j);
   vec3 color;
   printf("Obtaining color for Ray[%d,%d]\n", i,j);
   getColor(color, scene, ray, maxDepth);
   ys.push_back(color);
  }
  colors.push_back(ys);
 }
 printImage(colors, width, height, outputFile);
}

void Parser::getColor(vec3& color, Scene scene, Ray ray, int numBounces)
{
 Intersection inter; scene.intersect(inter,ray);
 if(inter.isIntersecting()){
  Color c; inter.getColor(c);
  c.getAmbient(color);
 } else {
  color = vec3(0,0,0);
 }
}

现在,我已经放弃了真正的光线跟踪部分,而只是简单地返回第一个对象的颜色,如果有的话。毫无疑问,当计算机知道光线与物体相交的唯一方法是通过Scene.intersect(),我也包括它。使用的成员变量是“矢量对象”参见结束

好的,现在出问题了。我首先创建一个场景并用Parser :: trace()方法之外的对象填充它。现在出于一些奇怪的原因,我为了i = j = 0投射Ray,一切都运行得非常好。但是,到第二条光线投射时,我场景中存储的所有对象都不再识别它们的vfptr(也就是说,我仍然可以访问除虚拟场景之外的所有SceneObject方法)!我使用调试器逐步调试代码,发现所有vfptr的信息都在getColor()结束和循环延续之间的某处丢失。但是,如果我更改getColor()的参数以使用Scene&amp;而不是场景,然后不会发生损失。这是多么疯狂的伏都教?

场景代码,按要求:

#include <vector>
#include <limits>
#include "Intersection.h"
#include "LightSource.h"
#include "SceneObject.h"

using namespace std;

/**
Contains a list of scene objects.  A ray can be 
intersected with a scene to find its color
*/
class Scene
{
public:
    vector<SceneObject*> objects;
    vector<LightSource*> lights;

    Scene(void);
    ~Scene(void);

    /** 
    Add an object to the scene
    */
    void addObject(SceneObject& o);

    /**
    Add a light source to the scene
    */
    void addLight(LightSource& l);

    /**
    Fill 'l' with all light sources in the scene
    */
    void getLightSources(vector<LightSource*>& l);

    /**
    Fills 'i' with information regarding an
    intersection with the closest object in the scene
    IF there is an intersection.  Check i.isIntersecting()
    to see if an intersection was actually found.
    */
    void intersect(Intersection& i, Ray r);

    void print();
};

#include "Scene.h"

Scene::Scene(void)
{
}

Scene::~Scene(void)
{
    for(int i=0;i<objects.size();i++){
        delete objects[i];
    }
    for(int i=0;i<lights.size();i++){
        delete lights[i];
    }
}

void Scene::addObject(SceneObject& o)
{
    objects.push_back(o.clone());
}

void Scene::addLight(LightSource& l)
{
    lights.push_back(l.clone());
}

void Scene::getLightSources(vector<LightSource*>& l)
{
    l = lights;
}

void Scene::intersect(Intersection& i, Ray r)
{
    Intersection result;
    result.setDistance(numeric_limits<double>::infinity());
    result.setIsIntersecting(false);

    double oldDist; result.getDistance(oldDist);

    /* Cycle through all objects, making result
    the closest one */
    for(int ind=0; ind<objects.size(); ind++){
        SceneObject* thisObj = objects[ind];
        Intersection betterIntersect;
        thisObj->intersect(betterIntersect, r);

        double newDist; betterIntersect.getDistance(newDist);
        if (newDist < oldDist){
            result = betterIntersect;
            oldDist = newDist;
        }
    }

    i = result;
}

void Scene::print()
{
    printf("%d Objects:\n", objects.size());
    for(int i=0;i<objects.size();i++){
        objects[i]->print();
    }
}

1 个答案:

答案 0 :(得分:5)

问题是您在SceneObjects的析构函数中删除了Scene并使用了默认的复制构造函数,它使用指针向量进行平面复制。这意味着,Scene的每个副本都引用相同的SceneObjects。如果其中一个Scene被破坏,它们都将丢失其引用的对象。如果您通过引用传递场景,这没有问题,因为在这种情况下,不会复制并随后销毁。