2017-10-19 9 views
7

J'ai un objet OpenGL dans une classe C++. Puisque j'utilise RAII, je veux que le destructeur le supprime. Donc, ma classe ressemble à quelque chose comme:L'objet OpenGL dans la classe C++ RAII ne fonctionne plus

class BufferObject 
{ 
private: 
    GLuint buff_; 

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

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

//Other members. 
}; 

Cela semble fonctionner. Mais chaque fois que je fais une des conditions suivantes, je commence à obtenir diverses erreurs OpenGL lorsque je l'utilise:

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 se passe-t-il?

Note: ceci est une tentative de construire une réponse canonique à ces questions.

Répondre

9

Toutes ces opérations copient l'objet C++. Puisque votre classe n'a pas défini de constructeur de copie, vous obtenez le constructeur de copie généré par le compilateur. Cela copie simplement tous les membres de l'objet.

Tenir compte le premier exemple:

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

Lorsque vous appelez push_back, il copie some_buffer dans un BufferObject dans le vector. Donc, juste avant de quitter cette portée, il y a deux objets BufferObject.

Mais quel objet tampon OpenGL stockent-ils? Eh bien, ils stockent le même. Après tout, en C++, nous avons juste copié un entier. Les deux objets C++ stockent donc la même valeur entière. Lorsque nous quittons cette portée, some_buffer sera détruit. Par conséquent, il appellera glDeleteBuffers sur cet objet OpenGL. Mais l'objet dans le vecteur aura toujours sa propre copie de ce nom d'objet OpenGL. Qui a été détruit.

Donc vous ne pouvez plus l'utiliser; d'où les erreurs.

La même chose se produit avec votre fonction InitBuffer. buff sera détruit après qu'il a été copié dans la valeur de retour, ce qui rend l'objet retourné sans valeur.

Tout cela est dû à une violation de la "règle de 3/5" en C++. Vous avez créé un destructeur sans créer de copier/déplacer des constructeurs/opérateurs d'affectation. C'est mauvais.

Pour résoudre ce problème, vos wrappers d'objets OpenGL doivent être des types à déplacement uniquement. Vous devez supprimer le constructeur de copie et de copie opérateur d'affectation, et fournir des équivalents de déplacement qui fixent le déplacé-d'un objet à 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. 
}; 

Il y a various other techniques pour faire des emballages de RAII déplacer uniquement pour les objets OpenGL.