当前位置: 首页 > 知识库问答 >
问题:

C RAII类中的OpenGL对象不再工作

牛昱
2023-03-14

我在C类中有一个OpenGL对象。因为我正在使用RAII,所以我想让析构函数删除它。所以我的课看起来像:

class BufferObject
{
private:
  GLuint buff_;

public:
  BufferObject()
  {
    glGenBuffers(1, &buff_);
  }

  ~BufferObject()
  {
    glDeleteBuffers(1, &buff_);
  }

//Other members.
};

这似乎有效。但每当我执行以下操作时,我在使用OpenGL时就会出现各种错误:

vector<BufferObject> bufVec;
{
  BufferObject some_buffer;
  //Initialize some_buffer;
  bufVec.push_back(some_buffer);
}
bufVec.back(); //buffer doesn't work.

BufferObject InitBuffer()
{
  BufferObject buff;
  //Do stuff with `buff`
  return buff;
}

auto buff = InitBuffer(); //Returned buffer doesn't work.

发生什么事?

注意:这是试图建立一个规范的答案,这些问题。

共有2个答案

马魁
2023-03-14

所有操作都复制缓冲区对象。但是由于你的类没有拷贝构造函数,它只是一个浅拷贝。由于析构函数删除缓冲区而不进一步检查,缓冲区将与原始对象一起删除。Nicol Bolas建议定义一个移动构造函数,删除复制构造函数和复制赋值操作符,我会描述一种不同的方式,这样两个缓冲区在复制后都可以使用。

您可以使用std::map数组轻松跟踪有多少对象使用单个对象。考虑下面的示例代码,这是代码的扩展:

#include <map>

std::map<unsigned int, unsigned int> reference_count;

class BufferObject
{
private:
    GLuint buff_;

public:
    BufferObject()
    {
        glGenBuffers(1, &buff_);
        reference_count[buff_] = 1; // Set reference count to it's initial value 1
    }

    ~BufferObject()
    {
        reference_count[buff_]--; // Decrease reference count
        if (reference_count[buff_] <= 0) // If reference count is zero, the buffer is no longer needed
            glDeleteBuffers(1, &buff_);
    }
    
    BufferObject(const BufferObject& other) : buff_(other.buff_)
    {
        reference_count[buff_]++; // Increase reference count
    }
    
    BufferObject operator = (const BufferObject& other)
    {
        if (buff_ != other.buff_) { // Check if both buffer is same
            buff_ = other.buff_;
            reference_count[buff_]++; // Increase reference count
        }
    }

// Other stuffs
};

代码是相当自我解释的。初始化缓冲区对象时,将创建一个新的缓冲区。然后构造函数在reference\u count数组中创建一个新元素,将缓冲区作为键,并将其值设置为1。每当复制对象时,计数都会增加。当对象被破坏时,计数减少。然后析构函数检查计数是否为0或更少,这意味着不再需要缓冲区,因此将删除缓冲区。

我建议不要将实现(或者至少是reference\u count数组)放在头文件中,这样就不会生成链接器错误。

齐夕
2023-03-14

所有这些操作都复制C对象。因为您的类没有定义复制构造函数,所以您得到了编译器生成的复制构造函数。这只是复制对象的所有成员。

考虑第一个例子:

vector<BufferObject> bufVec;
{
  BufferObject some_buffer;
  //Initialize some_buffer;
  bufVec.push_back(some_buffer);
}
bufVec.back(); //buffer doesn't work.

当您调用push_back时,它会将一些缓冲区复制到向量中的缓冲区对象。因此,在我们退出该范围之前,有两个BufferObject对象。

但是他们存储了什么OpenGL缓冲区对象呢?嗯,它们都是一样的。毕竟,对于C,我们只是复制了一个整数。因此,两个C对象存储相同的整数值。

当我们退出该范围时,some_buffer将被销毁。因此,它将在这个OpenGL对象上调用glDeleteBuffers。但是向量中的对象仍然有它自己的OpenGL对象名称副本。已经被摧毁了。

所以你不能再使用它了;因此出现了错误。

InitBuffer函数也会发生同样的情况buff复制到返回值后将被销毁,这使得返回的对象毫无价值。

这都是因为违反了C中所谓的“3/5规则”。创建析构函数时没有创建复制/移动构造函数/赋值操作符。那很糟糕。

为了解决这个问题,您的OpenGL对象包装器应该是只移动类型。您应该删除复制构造函数和复制赋值操作符,并提供将move-from对象设置为对象0的移动等价物:

class BufferObject
{
private:
  GLuint buff_;

public:
  BufferObject()
  {
    glGenBuffers(1, &buff_);
  }

  BufferObject(const BufferObject &) = delete;
  BufferObject &operator=(const BufferObject &) = delete;

  BufferObject(BufferObject &&other) : buff_(other.buff_)
  {
    other.buff_ = 0;
  }

  BufferObject &operator=(BufferObject &&other)
  {
    //ALWAYS check for self-assignment
    if(this != &other)
    {
      Release();
      buff_ = other.buff_;
      other.buff_ = 0;
    }

    return *this;
  }

  ~BufferObject() {Release();}

  void Release();
  {
    if(buff_)
      glDeleteBuffers(1, &buff_);
  }

//Other members.
};

为OpenGL对象制作仅移动RAII包装器还有各种其他技术。

 类似资料:
  • 所以我有一个立方体,我想绕着它的三个轴(立方体的轴,而不是窗口)旋转。正如许多其他类似的问题所指出的,只要我只朝一个方向旋转,我的旋转就会起作用,但当我开始混合它们时,我会得到奇怪的结果。特别是,无论立方体如何旋转,围绕y轴的旋转始终围绕窗口的y轴旋转。 我的绘图代码如下: 这个问题似乎非常准确地描述了我想做什么,公认的答案似乎是我想做什么,但是他提供的链接已经死了。 从我在链接问题中收集到的信息

  • 我正在开发一些游戏,以便学习OpenGL。 我正在尝试实现一种方法来拾取对象(例如,镐、枪等)。 这个想法是,即使在相机移动或旋转之后,这个物体也会固定在相机前面。 当我向前、向后、向左、向右、向上或向下移动时,我可以通过在相机正前方设置对象的位置来使对象跟随我。 然而...当相机旋转/倾斜(向上、向下、向左、向右看...)问题就开始了。 在这种情况下,一切都很好(向上、向下、向左、向右、向右倾斜

  • 我有一个奇怪的问题,每天我的AJAX请求一个网站不再工作。 我现在正在努力使它工作,但找不到问题。 这是我的JavaScript:基本上它非常简单,它检索ip adres并将其发送到存储它的站点。 在服务器上,我现在添加了这一项,以避免使用通配符 当我只使用 头('access-control-allow-origin:');我得到错误:Cross-Origin-request blocked:C

  • 我在Android Studio工作,多年来一直在正常模式和调试模式下愉快地运行。突然,调试模式对我来说停止了工作。 如果我尝试单击调试按钮(带有“播放”三角形的小错误),它会尝试在模拟器中启动,但应用程序永远不会打开。 在模拟器上,我得到了“等待调试器”的警告,以及“强制关闭”的选项。在Android Studio本身,底部的调试窗口反复显示“等待应用程序上线:”然后“无法连接到远程进程。正在中

  • 考虑下面的代码: 对于上面的代码,输出是:

  • 我使用Java和OpenGL(LWJGL)来设置一些矩阵,我不想使用内置的方法,因为我也想在Android上工作,所以使用LWJGL的矩阵类是不合适的。目前我正在建立一个透视图,使用一个视场70,znear 0.1,zfar 1000。使用当前设置旋转只会导致奇怪的结果,不是以正确的方式旋转,对象被奇怪地缩放,经常消失。 下面是Matrix4D类: 下面是Matrix类(用于在矩阵上设置和执行计算