【问题标题】:Not sure what's wrong -- Program Segfaults on two occasions不知道出了什么问题——两次程序段错误
【发布时间】:2013-03-13 11:03:17
【问题描述】:

我正在使用 SDL 创建一个非常简单的 Pong 游戏。对于碰撞检测,我有一个名为 DetectCollision 的类,如下所示:

class DetectCollision {
    public:
        std::vector<Object*> objects;
        int numOfObjects;

        DetectCollision();
        ~DetectCollision();

        void takeObjs(Object &);
        void handleCollision();
};

所以发生的事情是 takeObjs(Object &) 函数获取 Object 类的一个对象并将其存储在一个向量中。 takeObjs(Object &) 看起来像这样:

void DetectCollision::takeObjs(Object &obj1){
    objects.push_back(&obj1);
}

到目前为止一切正常。我可以访问向量“对象”来检测所有的碰撞,它工作正常,但是当我试图删除向量时问题就来了。如果我没记错的话,它是一个指针向量。所以我所做的删除它是在类析构函数中:

DetectCollision::~DetectCollision(){
    for (unsigned int i = 0; i < objects.size(); ++i){
        delete objects[i];
    }
    objects.clear();
}

根据使用 GCC 编译器的 Code::Blocks IDE,它在“delete objects[i];”上出现段错误线。它还会在程序的另一部分出现段错误,以尝试 SDL_FreeSurface() 类持有的皮肤,但我认为我可以很容易地修复它。这个向量是主要问题。如果您需要查看完整的源代码以帮助我了解如何解决问题,我可以提供。我非常感谢任何帮助。感谢和 DFTBA!

【问题讨论】:

    标签: c++ class pointers vector segmentation-fault


    【解决方案1】:

    您的应用程序出现段错误,因为调用析构函数时对象不再存在。

    takeObjs 方法接收对对象的引用,然后将指向该对象的指针存储在向量中。该对象在其他地方定义。当该对象超出范围时,它会自动销毁。

    当你到达析构函数时,对象就被销毁了。该应用程序出现段错误,因为您试图再次销毁它。

    您应该阅读 C++ 中对象的范围 (read the answer to this question),还应该阅读 Object destruction in C++

    编辑:添加简短示例来说明崩溃

    #include <iostream>
    #include <vector>
    using namespace std;
    
    class Object {
    public:
        int a;
        int b;
    
        Object(int a, int b)
        {
            this->a=a;
            this->b=b;
        }
    };
    
    class Test
    {
        std::vector<Object*> objects;
    public:
        Test(){}
    
        void Add(Object &obj)
        {
            objects.push_back(&obj);
        }
    
        void Print()
        {
            for(unsigned int i=0;i<objects.size();i++)
            {
                cout<<objects[i]->a<<" "<<objects[i]->b<<endl;
            }
        }
    
        ~Test()
        {
            for (unsigned int i = 0; i < objects.size(); ++i){
                delete objects[i];
            }
            objects.clear();
        }
    };
    
    void AddNewObjects(Test &t)
    {
        Object x(1,2);
        Object y(3,4);
        t.Add(x);
        t.Add(y);
        // you can access your objects here
        t.Print();
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        Test t;
        AddNewObjects(t);
        // but if you try to access the objects here, you get a crash
        // because the objects were destroyed when exiting "AddNewObjects"
        t.Print();
        return 0;
        // your destructor tries to access the objects here (in order to destroy them)
        // and that's why it crashes
    }
    

    这是您可以用来解决问题的一种解决方案:

    #include <iostream>
    #include <vector>
    using namespace std;
    
    class Object {
    public:
        int a;
        int b;
    
        Object(int a, int b)
        {
            this->a=a;
            this->b=b;
        }
    };
    
    class Test
    {
        std::vector<Object*> objects;
    public:
        Test(){}
    
        void Add(Object *pObj)
        {
            objects.push_back(pObj);
        }
    
        void Print()
        {
            for(unsigned int i=0;i<objects.size();i++)
            {
                cout<<objects[i]->a<<" "<<objects[i]->b<<endl;
            }
        }
    
        ~Test()
        {
            for (unsigned int i = 0; i < objects.size(); ++i){
                delete objects[i];
            }
            objects.clear();
        }
    };
    
    void AddNewObjects(Test &t)
    {
        Object* x = new Object(1,2);
        Object* y = new Object(3,4);
        t.Add(x);
        t.Add(y);
        // you can access your objects here
        t.Print();
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        Test t;
        AddNewObjects(t);
        // you can also access the objects here
        // because they are not destroyed anymore when exiting "AddNewObjects"
        t.Print();
        return 0;
    }
    

    【讨论】:

    • 如果对象在离开takeObjs的作用域后被销毁,那么程序的其余部分如何访问对象的向量并使用它的元素呢?什么时候毁灭?
    • 因为它访问堆栈位置,您是否在添加之前分配了对象?
    • 对象离开takeObjs的范围后没有被销毁。在您的程序完成对元素的访问之后,但在您的析构函数开始运行之前,该对象将被销毁。
    • @Jpon9 我添加了一个简短的示例来说明为什么会出现段错误。
    • 非常感谢,欧维!你的解决方案有效,我让它在小范围内工作。但是,在我的游戏中,由于我无法弄清楚的几个原因,它仍然会在关闭时出现段错误。 CodeBlocks 还在 libstdc++-6!_ZN9__gnu_cx18__exchange_and_add_EPVii() 函数上放置了一个红条,这对我来说看起来像是胡言乱语。它还指向 Object 类的空析构函数,说它会导致段错误。但除此之外,它还返回 -1073741819。如果有人想进一步帮助我,我将不胜感激。如果有人愿意,我可以提供其他联系信息和源代码......我真的被困在这里了。
    【解决方案2】:

    一个潜在的问题是DetectCollision 违反了Rule of Three。如果复制了一个实例,则该副本将获得相同Object 指针的向量,最终导致双deletes。

    【讨论】:

      【解决方案3】:

      您可能有双重删除,因为传递给您的 DetectCollision 类的对象在其他地方被删除,可能是因为它们超出了范围。

      您必须考虑谁将“拥有”这些对象。只有所有者才能删除它们。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多