OpenGL object in C++ RAII class no longer works
I have an OpenGL object in a C++ class. Since I'm employing RAII, I want to have the destructor delete it. So my class looks something like:
class BufferObject
private:
GLuint buff_;
public:
BufferObject()
glGenBuffers(1, &buff_);
~BufferObject()
glDeleteBuffers(1, &buff_);
//Other members.
;
This seems like it works. But any time I do any of the following, I start getting various OpenGL errors when I use it:
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.
What's going on?
Note: this is an attempt to build a canonical answer to these questions.
c++ c++11 opengl
add a comment |
I have an OpenGL object in a C++ class. Since I'm employing RAII, I want to have the destructor delete it. So my class looks something like:
class BufferObject
private:
GLuint buff_;
public:
BufferObject()
glGenBuffers(1, &buff_);
~BufferObject()
glDeleteBuffers(1, &buff_);
//Other members.
;
This seems like it works. But any time I do any of the following, I start getting various OpenGL errors when I use it:
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.
What's going on?
Note: this is an attempt to build a canonical answer to these questions.
c++ c++11 opengl
add a comment |
I have an OpenGL object in a C++ class. Since I'm employing RAII, I want to have the destructor delete it. So my class looks something like:
class BufferObject
private:
GLuint buff_;
public:
BufferObject()
glGenBuffers(1, &buff_);
~BufferObject()
glDeleteBuffers(1, &buff_);
//Other members.
;
This seems like it works. But any time I do any of the following, I start getting various OpenGL errors when I use it:
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.
What's going on?
Note: this is an attempt to build a canonical answer to these questions.
c++ c++11 opengl
I have an OpenGL object in a C++ class. Since I'm employing RAII, I want to have the destructor delete it. So my class looks something like:
class BufferObject
private:
GLuint buff_;
public:
BufferObject()
glGenBuffers(1, &buff_);
~BufferObject()
glDeleteBuffers(1, &buff_);
//Other members.
;
This seems like it works. But any time I do any of the following, I start getting various OpenGL errors when I use it:
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.
What's going on?
Note: this is an attempt to build a canonical answer to these questions.
c++ c++11 opengl
c++ c++11 opengl
edited Oct 22 '17 at 17:07
Vallentin
11.4k43149
11.4k43149
asked Oct 19 '17 at 22:04
Nicol BolasNicol Bolas
286k33474648
286k33474648
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
All of those operations copy the C++ object. Since your class did not define a copy constructor, you get the compiler-generated copy constructor. This simply copies all of the members of the object.
Consider the first example:
vector<BufferObject> bufVec;
BufferObject some_buffer;
//Initialize some_buffer;
bufVec.push_back(some_buffer);
bufVec.back(); //buffer doesn't work.
When you call push_back
, it copies some_buffer
into a BufferObject
in the vector
. So, right before we exit that scope, there are two BufferObject
objects.
But what OpenGL buffer object do they store? Well, they store the same one. After all, to C++, we just copied an integer. So both C++ objects store the same integer value.
When we exit that scope, some_buffer
will be destroyed. Therefore, it will call glDeleteBuffers
on this OpenGL object. But the object in the vector will still have its own copy of that OpenGL object name. Which has been destroyed.
So you can't use it anymore; hence the errors.
The same thing happens with your InitBuffer
function. buff
will be destroyed after it is copied into the return value, which makes the returned object worthless.
This is all due to a violation of the so-called "Rule of 3/5" in C++. You created a destructor without creating copy/move constructors/assignment operators. That's bad.
To solve this, your OpenGL object wrappers should be move-only types. You should delete the copy constructor and copy assignment operator, and provide move equivalents that set the moved-from object to object 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.
;
There are various other techniques for making move-only RAII wrappers for OpenGL objects.
add a comment |
Your Answer
StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");
StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);
else
createEditor();
);
function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader:
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
,
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f46839586%2fopengl-object-in-c-raii-class-no-longer-works%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
All of those operations copy the C++ object. Since your class did not define a copy constructor, you get the compiler-generated copy constructor. This simply copies all of the members of the object.
Consider the first example:
vector<BufferObject> bufVec;
BufferObject some_buffer;
//Initialize some_buffer;
bufVec.push_back(some_buffer);
bufVec.back(); //buffer doesn't work.
When you call push_back
, it copies some_buffer
into a BufferObject
in the vector
. So, right before we exit that scope, there are two BufferObject
objects.
But what OpenGL buffer object do they store? Well, they store the same one. After all, to C++, we just copied an integer. So both C++ objects store the same integer value.
When we exit that scope, some_buffer
will be destroyed. Therefore, it will call glDeleteBuffers
on this OpenGL object. But the object in the vector will still have its own copy of that OpenGL object name. Which has been destroyed.
So you can't use it anymore; hence the errors.
The same thing happens with your InitBuffer
function. buff
will be destroyed after it is copied into the return value, which makes the returned object worthless.
This is all due to a violation of the so-called "Rule of 3/5" in C++. You created a destructor without creating copy/move constructors/assignment operators. That's bad.
To solve this, your OpenGL object wrappers should be move-only types. You should delete the copy constructor and copy assignment operator, and provide move equivalents that set the moved-from object to object 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.
;
There are various other techniques for making move-only RAII wrappers for OpenGL objects.
add a comment |
All of those operations copy the C++ object. Since your class did not define a copy constructor, you get the compiler-generated copy constructor. This simply copies all of the members of the object.
Consider the first example:
vector<BufferObject> bufVec;
BufferObject some_buffer;
//Initialize some_buffer;
bufVec.push_back(some_buffer);
bufVec.back(); //buffer doesn't work.
When you call push_back
, it copies some_buffer
into a BufferObject
in the vector
. So, right before we exit that scope, there are two BufferObject
objects.
But what OpenGL buffer object do they store? Well, they store the same one. After all, to C++, we just copied an integer. So both C++ objects store the same integer value.
When we exit that scope, some_buffer
will be destroyed. Therefore, it will call glDeleteBuffers
on this OpenGL object. But the object in the vector will still have its own copy of that OpenGL object name. Which has been destroyed.
So you can't use it anymore; hence the errors.
The same thing happens with your InitBuffer
function. buff
will be destroyed after it is copied into the return value, which makes the returned object worthless.
This is all due to a violation of the so-called "Rule of 3/5" in C++. You created a destructor without creating copy/move constructors/assignment operators. That's bad.
To solve this, your OpenGL object wrappers should be move-only types. You should delete the copy constructor and copy assignment operator, and provide move equivalents that set the moved-from object to object 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.
;
There are various other techniques for making move-only RAII wrappers for OpenGL objects.
add a comment |
All of those operations copy the C++ object. Since your class did not define a copy constructor, you get the compiler-generated copy constructor. This simply copies all of the members of the object.
Consider the first example:
vector<BufferObject> bufVec;
BufferObject some_buffer;
//Initialize some_buffer;
bufVec.push_back(some_buffer);
bufVec.back(); //buffer doesn't work.
When you call push_back
, it copies some_buffer
into a BufferObject
in the vector
. So, right before we exit that scope, there are two BufferObject
objects.
But what OpenGL buffer object do they store? Well, they store the same one. After all, to C++, we just copied an integer. So both C++ objects store the same integer value.
When we exit that scope, some_buffer
will be destroyed. Therefore, it will call glDeleteBuffers
on this OpenGL object. But the object in the vector will still have its own copy of that OpenGL object name. Which has been destroyed.
So you can't use it anymore; hence the errors.
The same thing happens with your InitBuffer
function. buff
will be destroyed after it is copied into the return value, which makes the returned object worthless.
This is all due to a violation of the so-called "Rule of 3/5" in C++. You created a destructor without creating copy/move constructors/assignment operators. That's bad.
To solve this, your OpenGL object wrappers should be move-only types. You should delete the copy constructor and copy assignment operator, and provide move equivalents that set the moved-from object to object 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.
;
There are various other techniques for making move-only RAII wrappers for OpenGL objects.
All of those operations copy the C++ object. Since your class did not define a copy constructor, you get the compiler-generated copy constructor. This simply copies all of the members of the object.
Consider the first example:
vector<BufferObject> bufVec;
BufferObject some_buffer;
//Initialize some_buffer;
bufVec.push_back(some_buffer);
bufVec.back(); //buffer doesn't work.
When you call push_back
, it copies some_buffer
into a BufferObject
in the vector
. So, right before we exit that scope, there are two BufferObject
objects.
But what OpenGL buffer object do they store? Well, they store the same one. After all, to C++, we just copied an integer. So both C++ objects store the same integer value.
When we exit that scope, some_buffer
will be destroyed. Therefore, it will call glDeleteBuffers
on this OpenGL object. But the object in the vector will still have its own copy of that OpenGL object name. Which has been destroyed.
So you can't use it anymore; hence the errors.
The same thing happens with your InitBuffer
function. buff
will be destroyed after it is copied into the return value, which makes the returned object worthless.
This is all due to a violation of the so-called "Rule of 3/5" in C++. You created a destructor without creating copy/move constructors/assignment operators. That's bad.
To solve this, your OpenGL object wrappers should be move-only types. You should delete the copy constructor and copy assignment operator, and provide move equivalents that set the moved-from object to object 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.
;
There are various other techniques for making move-only RAII wrappers for OpenGL objects.
edited Oct 22 '17 at 17:07
Vallentin
11.4k43149
11.4k43149
answered Oct 19 '17 at 22:04
Nicol BolasNicol Bolas
286k33474648
286k33474648
add a comment |
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f46839586%2fopengl-object-in-c-raii-class-no-longer-works%23new-answer', 'question_page');
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown