OpenGL object in C++ RAII class no longer works










9















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.










share|improve this question




























    9















    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.










    share|improve this question


























      9












      9








      9


      3






      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.










      share|improve this question
















      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






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Oct 22 '17 at 17:07









      Vallentin

      11.4k43149




      11.4k43149










      asked Oct 19 '17 at 22:04









      Nicol BolasNicol Bolas

      286k33474648




      286k33474648






















          1 Answer
          1






          active

          oldest

          votes


















          11














          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.






          share|improve this answer
























            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
            );



            );













            draft saved

            draft discarded


















            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









            11














            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.






            share|improve this answer





























              11














              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.






              share|improve this answer



























                11












                11








                11







                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.






                share|improve this answer















                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.







                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Oct 22 '17 at 17:07









                Vallentin

                11.4k43149




                11.4k43149










                answered Oct 19 '17 at 22:04









                Nicol BolasNicol Bolas

                286k33474648




                286k33474648



























                    draft saved

                    draft discarded
















































                    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.




                    draft saved


                    draft discarded














                    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





















































                    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







                    這個網誌中的熱門文章

                    Barbados

                    How to read a connectionString WITH PROVIDER in .NET Core?

                    Node.js Script on GitHub Pages or Amazon S3