【问题标题】:Why does this code throw a read access violation?为什么此代码会引发读取访问冲突?
【发布时间】:2020-12-28 11:53:14
【问题描述】:

我需要帮助弄清楚为什么这段代码会引发读取访问冲突

代码在数组的第 4 个值处引发读取访问冲突,这在我在第 7 行和第 8 行添加动态内存分配时开始发生

ProceduralStories.cpp

#include <iostream>

#include "ProceduralStories.h"

void main()
{
    Object* _objects = NULL;
    _objects = new Object[10];

    for (int i = 0; i < 10; i++)
    {
        Object object = Object
        {
            object.position = glm::vec3 {rand() % 1000 + 1, rand() % 1000 + 1, rand() % 1000 + 1},
            object.mesh = i
        };

        _objects[i] = object;
    }

    Location location1
    {
        location1.position = glm::vec3 {0.0f, 0.0f, 0.0f},
    };

    for (int i = 0; i < 10; i++)
    {
        location1.objects[i] = _objects[i]; //Throws a Read Access Violation at the 4th value of the array, so [3]
        std::cout << location1.objects[i].mesh << ", (" << location1.objects[i].position.x << ',' << location1.objects[i].position.y << ',' << location1.objects[i].position.z << ')' << std::endl;
    }
}

ProceduralStories.h


#include "glm/vec3.hpp"

struct Object
{
    glm::vec3 position;
    int mesh;
};

struct Location
{
    glm::vec3 position;
    Object objects[];
};

【问题讨论】:

  • location1 在第 21 行的构造中没有为对象分配空间。正如大卫在他的回答中所说。你也有内存泄漏。 _objects 是新的,但从未被删除。

标签: c++ arrays memory-management


【解决方案1】:

您从未为objects[] 分配任何空间。因此,当您尝试将 location1.objects[i] 设置为某个值时,您正在写入尚未分配的空间。

如果您认为您确实分配了空间,请告诉我您分配空间的位置和数量。你做不到。

【讨论】:

    【解决方案2】:

    请不要这样做。

    花点时间,学习写一个 RAII 类。

    如果你写新的,你必须调用 delete,否则你会在更大的项目中泄漏内存。

    当对象超出范围时,析构函数会为您执行此操作。但是在这种情况下,您调用默认析构函数,它只调用子析构函数,并且指针不会自行清理。

    #include<iostream>
    struct loc{
        int pos;
        int*obj;
    };
    void run();
    int main(){
       run();
       return 0;
    }
    void run()
    {
        using namespace std;
        loc t{ 42, new int[10]};
    
        cout<<t.pos<<endl;
    
        t.obj[0] = 5;
        t.obj[9] = 3;
    
        for(int i = 0; i < 10; i++)
        {
            cout<<t.obj[i]<<endl;
        }
        delete[] t.obj;
    }
    

    编辑:我觉得有责任提供更好的选择。

    我不一定认为这是最好的解决方案,但正如我提到的 RAII,我会给你一些东西。

    #include<iostream>
    class loc{
     public:
        int pos;
     private:
        unsigned long size;
        int*obj;
     public:
        loc(): pos{0},size{},obj{nullptr}
        {
        }
        loc(int pos, unsigned long size):pos{pos},size{size}
        {
            obj = new int[size];
        }
        loc(loc&)=delete;
        loc(loc&&)=delete;
        loc operator=(loc&)=delete;
        loc operator=(loc&&)=delete;
        ~loc()
        {
            delete[] obj;
        }
        int&operator[](unsigned long index)
        {
            if(index > size) throw std::out_of_range
            ( "Index "
            + std::to_string(index)
            + " excededs allocated size "
            + std::to_string(size)
            );
            return obj[index];
        }
    
    
    };
    void run()
    {
    using namespace std;
        loc t{ 42, 10};
    
        cout<<t.pos<<endl;
    
        t[0] = 5;
        t[9] = 3;
    
        for(int i = 0; i < 10; i++)
        {
            cout<<t[i]<<endl;
        }
        // destructor deleate memory for you.
    }
    

    请阅读复制移动赋值和移动赋值构造函数以及何时使用它们。由于这并没有,而且个人而言,它超出了问题的范围。

    【讨论】:

    • @user4581301 我不知道 RAII 是否合适,但我试了一下。谢谢你提醒我这个机会。如果在这种情况下有什么可以改进的,我欢迎编辑。
    • 更新是正确的。三个注意事项:new 在失败时抛出异常,除非特别要求不要这样做,因此您不必测试 null 并抛出自己的异常。当构造函数抛出时,无需清理诸如将int 归零之类的琐碎事情,因为调用者不会获取对象。您必须确保已释放拥有和非托管资源——直接返回 RAII;这些资源应该放回原处。在delete 之前不需要空检查,因为delete 正确处理空指针。
    • @user4581301 我采纳了你的建议。感谢您指出不必要的 if !
    猜你喜欢
    • 2019-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多