El objeto OpenGL en la clase C++ RAII ya no funciona
c++11 (1)
Tengo un objeto OpenGL en una clase de C ++. Como estoy empleando RAII, quiero que el destructor lo elimine. Entonces mi clase se parece a:
class BufferObject
{
private:
GLuint buff_;
public:
BufferObject()
{
glGenBuffers(1, &buff_);
}
~BufferObject()
{
glDeleteBuffers(1, &buff_);
}
//Other members.
};
Esto parece que funciona. Pero cada vez que hago algo de lo siguiente, empiezo a recibir varios errores de OpenGL cuando lo uso:
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.
¿Que esta pasando?
Nota: este es un intento de construir una respuesta canónica a estas preguntas.
Todas esas operaciones copian el objeto C ++. Como su clase no definió un constructor de copia, obtiene el constructor de copia generado por el compilador. Esto simplemente copia a todos los miembros del objeto.
Considere el primer ejemplo:
vector<BufferObject> bufVec;
{
BufferObject some_buffer;
//Initialize some_buffer;
bufVec.push_back(some_buffer);
}
bufVec.back(); //buffer doesn''t work.
Cuando llama a
push_back
, copia
some_buffer
en un
BufferObject
en el
vector
.
Entonces, justo antes de salir de ese ámbito, hay dos objetos
BufferObject
.
¿Pero qué objeto de tampón OpenGL almacenan? Bueno, almacenan el mismo . Después de todo, en C ++, simplemente copiamos un número entero. Entonces, ambos objetos C ++ almacenan el mismo valor entero.
Cuando
some_buffer
ese alcance, se destruirá
some_buffer
.
Por lo tanto, llamará a
glDeleteBuffers
en este objeto OpenGL.
Pero el objeto en el vector todavía tendrá su propia copia de ese nombre de objeto OpenGL.
Que ha
sido destruido
.
Entonces ya no puedes usarlo; De ahí los errores.
Lo mismo sucede con su función
InitBuffer
.
buff
se destruirá después de que se copia en el valor de retorno, lo que hace que el objeto devuelto no tenga valor.
Todo esto se debe a una violación de la llamada "Regla de 3/5" en C ++. Creó un destructor sin crear copia / mover constructores / operadores de asignación. Eso es malo.
Para resolver esto, sus contenedores de objetos OpenGL deben ser de solo movimiento. Debe eliminar el constructor de copia y el operador de asignación de copia, y proporcionar equivalentes de movimiento que establezcan el objeto movido del objeto 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.
};
Existen varias otras técnicas para hacer envoltorios RAII de solo movimiento para objetos OpenGL.