2017-10-19 38 views
7

我在C++类中有一个OpenGL对象。由于我正在使用RAII,因此我想让析构函数将其删除。所以我的班级看起来像这样: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. 

发生了什么事?

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

回答

9

所有这些操作都复制C++对象。由于你的类没有定义一个拷贝构造函数,你得到了编译器生成的拷贝构造函数。这只是复制对象的所有成员。

考虑的第一个例子:

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

当你调用push_back,它复制some_bufferBufferObjectvector。所以,在我们退出该范围之前,有两个BufferObject对象。

但他们存储什么OpenGL缓冲区对象?那么,他们存储是同一个。毕竟,对于C++,我们只是复制了一个整数。所以两个C++对象都存储相同的整数值。

当我们退出该范围时,some_buffer将被销毁。因此,它将在此OpenGL对象上调用glDeleteBuffers。但是矢量中的对象仍然有它自己的那个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制作唯才是举,RAII包装对OpenGL的对象。