2017-10-19 15 views
7

Tôi có đối tượng OpenGL trong lớp C++. Vì tôi đang sử dụng RAII, tôi muốn hủy bỏ nó. Vì vậy, lớp học của tôi trông giống như:Đối tượng OpenGL trong lớp C++ RAII không còn hoạt động

class BufferObject 
{ 
private: 
    GLuint buff_; 

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

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

//Other members. 
}; 

Điều này có vẻ như nó hoạt động. Nhưng bất cứ lúc nào tôi thực hiện bất kỳ thao tác nào sau đây, tôi bắt đầu gặp phải nhiều lỗi OpenGL khác nhau khi sử dụng:

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. 

Điều gì đang xảy ra?

Lưu ý: đây là một nỗ lực để xây dựng câu trả lời chuẩn cho những câu hỏi này.

Trả lời

9

Tất cả các thao tác đó sao chép đối tượng C++. Vì lớp của bạn không định nghĩa một hàm tạo bản sao, bạn sẽ nhận được hàm tạo bản sao được tạo bởi trình biên dịch. Điều này chỉ đơn giản là sao chép tất cả các thành viên của đối tượng.

xem xét ví dụ đầu tiên:

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

Khi bạn gọi push_back, nó sao chép some_buffer thành một BufferObject trong vector. Vì vậy, ngay trước khi chúng tôi thoát khỏi phạm vi đó, có hai đối tượng BufferObject.

Nhưng đối tượng bộ đệm OpenGL nào mà chúng lưu trữ? Vâng, họ lưu trữ cùng một số. Sau khi tất cả, để C + +, chúng tôi chỉ cần sao chép một số nguyên. Vì vậy, cả hai đối tượng C++ lưu trữ cùng một giá trị số nguyên.

Khi chúng tôi thoát khỏi phạm vi đó, some_buffer sẽ bị hủy. Do đó, nó sẽ gọi glDeleteBuffers trên đối tượng OpenGL này. Nhưng đối tượng trong vectơ vẫn sẽ có bản sao riêng của tên đối tượng OpenGL đó. Trong đó có bị hủy.

Vì vậy, bạn không thể sử dụng nó nữa; do đó các lỗi.

Điều tương tự cũng xảy ra với chức năng InitBuffer của bạn. buff sẽ bị hủy sau khi được sao chép vào giá trị trả về, điều này làm cho đối tượng trả về vô giá trị.

Tất cả điều này là do vi phạm "Quy tắc 3/5" trong C++. Bạn đã tạo ra một destructor mà không tạo các nhà xây dựng bản sao/di chuyển/chuyển nhượng. Thật tồi tệ.

Để giải quyết vấn đề này, trình bao bọc đối tượng OpenGL của bạn phải là các loại chỉ di chuyển. Bạn nên xóa constructor sao chép và sao chép toán tử gán, và cung cấp các khoản tương đương di chuyển chính xác đến các chuyển-từ đối tượng để đối tượng 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 để làm giấy gói RAII di chuyển chỉ dành cho đối tượng OpenGL.

Các vấn đề liên quan