在 C9803 中

在 C++ 98/03 中封装 OpenGL 对象需要遵守 C++规则 3.这意味着添加复制构造函数,复制赋值运算符和析构函数。

但是,复制构造函数应该在逻辑上复制该对象。复制 OpenGL 对象是一件非常重要的事情。同样重要的是,它几乎肯定是用户不希望做的事情。

因此,我们将使对象不可复制:

class BufferObject
{
public:
    BufferObject(GLenum target, GLsizeiptr size, const void *data, GLenum usage)
    {
        glGenBuffers(1, &object_);
        glBindBuffer(target, object_);
        glBufferData(target, size, data, usage);
        glBindBuffer(target, 0);
    }
    
    ~BufferObject()
    {
        glDeleteBuffers(1, &object_);
    }
    
    //Accessors and manipulators
    void Bind(GLenum target) const {glBindBuffer(target, object_);}
    GLuint GetObject() const {return object_;}

private:
    GLuint object_;
    
    //Prototypes, but no implementation.
    BufferObject(const BufferObject &);
    BufferObject &operator=(const BufferObject &);
};

构造函数将创建对象并初始化缓冲区对象的数据。析构函数将破坏对象。通过声明复制构造函数/赋值而不定义它们,如果任何代码试图调用它们,链接器将给出错误。通过宣称它们是私有的,只有 BufferObject 的成员甚至可以调用他们。

请注意,BufferObject 不会保留传递给构造函数的 target。这是因为 OpenGL 缓冲区对象可以与任何目标一起使用,而不仅仅是最初创建的目标。这与纹理对象不同,纹理对象必须始终绑定到最初创建的目标。

因为 OpenGL 非常依赖于将对象绑定到上下文以用于各种目的,所以具有 RAII 样式的范围对象绑定通常也是有用的。因为不同的对象具有不同的绑定需求(一些具有目标,而另一些没有),我们必须分别为每个对象实现一个。

class BindBuffer
{
public:
    BindBuffer(GLenum target, const BufferObject &buff) : target_(target)
    {
        buff.Bind(target_);
    }
    
    ~BindBuffer()
    {
        glBindBuffer(target_, 0);
    }
    
private:
    GLenum target_;

    //Also non-copyable.
    BindBuffer(const BindBuffer &);
    BindBuffer &operator=(const BindBuffer &);
};

BindBuffer 是不可复制的,因为复制它是没有意义的。请注意,它不会保留对它所绑定的 BufferObject 的访问权限。那是因为没必要。