我在 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.
发生了什么事?
注意:这是对这些问题建立规范答案的尝试。
最佳答案
所有这些操作都会复制 C++ 对象。由于您的类没有定义复制构造函数,因此您将获得编译器生成的复制构造函数。这只是复制对象的所有成员。
考虑第一个例子:
vector<BufferObject> bufVec;
{
BufferObject some_buffer;
//Initialize some_buffer;
bufVec.push_back(some_buffer);
}
bufVec.back(); //buffer doesn't work.
当您调用 push_back
时,它会将 some_buffer
复制到 vector
中的 BufferObject
中。因此,就在我们退出该范围之前,有两个 BufferObject
对象。
但它们存储的是什么 OpenGL 缓冲区对象?嗯,他们存储相同的。毕竟,对于 C++,我们只是复制了一个整数。所以两个 C++ 对象都存储相同的整数值。
当我们退出该范围时,some_buffer
将被销毁。因此,它将在这个 OpenGL 对象上调用 glDeleteBuffers
。但是 vector 中的对象仍然有它自己的那个 OpenGL 对象名称的拷贝。哪个被销毁了。
所以你不能再使用它了;因此出现错误。
InitBuffer
函数也会发生同样的事情。 buff
复制到返回值后会被销毁,使得返回的对象一文不值。
这都是因为违反了 C++ 中所谓的“3/5 规则”。您创建了一个析构函数,但没有创建复制/移动构造函数/赋值运算符。这很糟糕。
要解决这个问题,您的 OpenGL 对象包装器应该是仅移动类型。您应该删除复制构造函数和复制赋值运算符,并提供将移动对象设置为对象 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.
};
有various other techniques用于为 OpenGL 对象制作只能移动的 RAII 包装器。
关于c++ - C++ RAII 类中的 OpenGL 对象不再有效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46839586/