Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Special Interest Groups
  3. C++ Gurus
  4. trying to understand smart pointers...

trying to understand smart pointers...

Scheduled Pinned Locked Moved Solved C++ Gurus
30 Posts 6 Posters 4.6k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • Christian EhrlicherC Christian Ehrlicher

    @mzimmers said in trying to understand smart pointers...:

    Can they be modified after they're constructed?

    Yes, it's a normal pointer but you can't copy them - only moving is allowed. Your TestStruct is missing the move ctor and move operator.

    list2.append(uniquePtr); // how to form this?

    As I said - it's not possible with QList as a unique_ptr<T> is not copyable, only movable.

    mzimmersM Offline
    mzimmersM Offline
    mzimmers
    wrote on last edited by
    #11

    @Christian-Ehrlicher

    sigh I new I should have taken that extra C++ class in night school...

    OK, so I think I've added the move c'tor and move operator (though the docs are a little confusing; there's reference to a move assignment operator, which I'm not sure is the same thing). Code looks like this:

    struct TestStruct {
    	int i;
    	TestStruct() {}
    	TestStruct(TestStruct &ts) {i = ts.i;} // copy c'tor
    	TestStruct(TestStruct &&ts) { i = ts.i; } // move c'tor
    	TestStruct& operator=(TestStruct&& ts) { return *this; } // move operator
    };
    TestStruct myStruct;
    
    std::unique_ptr<TestStruct> *uniquePtr = nullptr;
    QList<std::unique_ptr<TestStruct>> list;
    uniquePtr = &myStruct; // how to form this?
    list.push_back(*uniquePtr); // how to form this?
    

    What am I continuing to do wrong here?

    Thanks...

    Christian EhrlicherC JoeCFDJ 2 Replies Last reply
    0
    • mzimmersM mzimmers

      @Christian-Ehrlicher

      sigh I new I should have taken that extra C++ class in night school...

      OK, so I think I've added the move c'tor and move operator (though the docs are a little confusing; there's reference to a move assignment operator, which I'm not sure is the same thing). Code looks like this:

      struct TestStruct {
      	int i;
      	TestStruct() {}
      	TestStruct(TestStruct &ts) {i = ts.i;} // copy c'tor
      	TestStruct(TestStruct &&ts) { i = ts.i; } // move c'tor
      	TestStruct& operator=(TestStruct&& ts) { return *this; } // move operator
      };
      TestStruct myStruct;
      
      std::unique_ptr<TestStruct> *uniquePtr = nullptr;
      QList<std::unique_ptr<TestStruct>> list;
      uniquePtr = &myStruct; // how to form this?
      list.push_back(*uniquePtr); // how to form this?
      

      What am I continuing to do wrong here?

      Thanks...

      Christian EhrlicherC Offline
      Christian EhrlicherC Offline
      Christian Ehrlicher
      Lifetime Qt Champion
      wrote on last edited by
      #12

      @mzimmers said in trying to understand smart pointers...:

      What am I continuing to do wrong here?

      You still use a QList - as I already told you QList needs a copyable type but std::unique_ptr<T> is not.

      Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
      Visit the Qt Academy at https://academy.qt.io/catalog

      mzimmersM 1 Reply Last reply
      1
      • Christian EhrlicherC Christian Ehrlicher

        @mzimmers said in trying to understand smart pointers...:

        What am I continuing to do wrong here?

        You still use a QList - as I already told you QList needs a copyable type but std::unique_ptr<T> is not.

        mzimmersM Offline
        mzimmersM Offline
        mzimmers
        wrote on last edited by
        #13

        @Christian-Ehrlicher said in trying to understand smart pointers...:

        QList needs a copyable type but std::unique_ptr<T> is not.

        Ah, yes. And it appears that QScopedPointer is not either...pity.

        So, if I use std::vector, and the make_unique that you mentioned above, I can modify what the unique_ptr references, and push it into the vector and it all works OK:

        typedef std::unique_ptr<TestStruct> UniquePtr;
        UniquePtr uniquePtr;
        std::vector<UniquePtr> qVector;
        
        uniquePtr = std::make_unique<TestStruct>(myStruct);
        
        uniquePtr->i = 55;
        qDebug() << uniquePtr->i;
        qVector.push_back(std::make_unique<TestStruct>(*uniquePtr));
        
        uniquePtr->i = 555;
        qDebug() << uniquePtr->i;
        qVector.push_back(std::make_unique<TestStruct>(*uniquePtr));
        
        uniquePtr->i = 5555;
        qVector.push_back(std::make_unique<TestStruct>(*uniquePtr));
        
        qDebug() << qVector.at(0)->i << qVector.at(1)->i << qVector.at(2)->i;
        

        Now: I want to reference elements in the vector, probably in a loop. Do I need to create a new unique_ptr for each loop iteration, since I can't do an assignment to it, or is there some other way to do this?

        Christian EhrlicherC 1 Reply Last reply
        0
        • mzimmersM mzimmers

          @Christian-Ehrlicher said in trying to understand smart pointers...:

          QList needs a copyable type but std::unique_ptr<T> is not.

          Ah, yes. And it appears that QScopedPointer is not either...pity.

          So, if I use std::vector, and the make_unique that you mentioned above, I can modify what the unique_ptr references, and push it into the vector and it all works OK:

          typedef std::unique_ptr<TestStruct> UniquePtr;
          UniquePtr uniquePtr;
          std::vector<UniquePtr> qVector;
          
          uniquePtr = std::make_unique<TestStruct>(myStruct);
          
          uniquePtr->i = 55;
          qDebug() << uniquePtr->i;
          qVector.push_back(std::make_unique<TestStruct>(*uniquePtr));
          
          uniquePtr->i = 555;
          qDebug() << uniquePtr->i;
          qVector.push_back(std::make_unique<TestStruct>(*uniquePtr));
          
          uniquePtr->i = 5555;
          qVector.push_back(std::make_unique<TestStruct>(*uniquePtr));
          
          qDebug() << qVector.at(0)->i << qVector.at(1)->i << qVector.at(2)->i;
          

          Now: I want to reference elements in the vector, probably in a loop. Do I need to create a new unique_ptr for each loop iteration, since I can't do an assignment to it, or is there some other way to do this?

          Christian EhrlicherC Offline
          Christian EhrlicherC Offline
          Christian Ehrlicher
          Lifetime Qt Champion
          wrote on last edited by
          #14

          @mzimmers said in trying to understand smart pointers...:

          Do I need to create a new unique_ptr for each loop iteration, since I can't do an assignment to it, or is there some other way to do this?

          I don't understand - you already access the elements (via at() ) and therefore can call the functions of the object (or modify the members of your struct)

          Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
          Visit the Qt Academy at https://academy.qt.io/catalog

          mzimmersM 1 Reply Last reply
          0
          • Christian EhrlicherC Christian Ehrlicher

            @mzimmers said in trying to understand smart pointers...:

            Do I need to create a new unique_ptr for each loop iteration, since I can't do an assignment to it, or is there some other way to do this?

            I don't understand - you already access the elements (via at() ) and therefore can call the functions of the object (or modify the members of your struct)

            mzimmersM Offline
            mzimmersM Offline
            mzimmers
            wrote on last edited by
            #15

            @Christian-Ehrlicher I'd like to be able to do the following:

            for (int i = 0; i < list.size(); i++) {
                p = list.at(i);
            	p.this = that; 
            	p.the_other(); // etc
            
            Christian EhrlicherC 1 Reply Last reply
            0
            • mzimmersM mzimmers

              @Christian-Ehrlicher I'd like to be able to do the following:

              for (int i = 0; i < list.size(); i++) {
                  p = list.at(i);
              	p.this = that; 
              	p.the_other(); // etc
              
              Christian EhrlicherC Offline
              Christian EhrlicherC Offline
              Christian Ehrlicher
              Lifetime Qt Champion
              wrote on last edited by
              #16

              Why do you want to assign something to the local variable p? What's the point?

              Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
              Visit the Qt Academy at https://academy.qt.io/catalog

              mzimmersM 1 Reply Last reply
              0
              • Christian EhrlicherC Christian Ehrlicher

                Why do you want to assign something to the local variable p? What's the point?

                mzimmersM Offline
                mzimmersM Offline
                mzimmers
                wrote on last edited by
                #17

                @Christian-Ehrlicher well, maybe I don't have to. The plan is to use this list in a list model I'm writing. I'll be making temporary variables of my struct, and copying to and from the list. (Plus QML access that I haven't even begun thinking about.) But...maybe I can do it all with newly-created unique_ptrs. I'll try and report back.

                BTW: should I consider the use of QScopedPointers instead?

                Christian EhrlicherC Chris KawaC 2 Replies Last reply
                0
                • mzimmersM mzimmers

                  @Christian-Ehrlicher well, maybe I don't have to. The plan is to use this list in a list model I'm writing. I'll be making temporary variables of my struct, and copying to and from the list. (Plus QML access that I haven't even begun thinking about.) But...maybe I can do it all with newly-created unique_ptrs. I'll try and report back.

                  BTW: should I consider the use of QScopedPointers instead?

                  Christian EhrlicherC Offline
                  Christian EhrlicherC Offline
                  Christian Ehrlicher
                  Lifetime Qt Champion
                  wrote on last edited by
                  #18

                  Simply store the struct in the container.

                  Qt Online Installer direct download: https://download.qt.io/official_releases/online_installers/
                  Visit the Qt Academy at https://academy.qt.io/catalog

                  1 Reply Last reply
                  0
                  • mzimmersM mzimmers

                    @Christian-Ehrlicher well, maybe I don't have to. The plan is to use this list in a list model I'm writing. I'll be making temporary variables of my struct, and copying to and from the list. (Plus QML access that I haven't even begun thinking about.) But...maybe I can do it all with newly-created unique_ptrs. I'll try and report back.

                    BTW: should I consider the use of QScopedPointers instead?

                    Chris KawaC Offline
                    Chris KawaC Offline
                    Chris Kawa
                    Lifetime Qt Champion
                    wrote on last edited by Chris Kawa
                    #19

                    @mzimmers said:

                    I'll be making temporary variables of my struct, and copying to and from the list.

                    The whole point of unique_ptr is that it is unique. It holds ownership of the object. You can't copy (temporary or otherwise) unique_ptr because then you would have two things owning the same object and that would just crash because of double delete.

                    You can move unique_ptrs, because it moves ownership of the object, so only one pointer still owns the object.

                    As others mentioned QList does not support move-only types because of implicit sharing. It needs to do copies underneath when a shared data detaches.

                    QScopedPointer is just a simplified version of std::unique_ptr. Switching one to the other doesn't change anything.

                    You can store unique_ptr in a std::vector, which does not do implicit sharing and supports move-only types:

                    std::vector<std::unique_ptr<QString>> pointers;
                    for (int i=0; i < 10; ++i)
                    {
                        pointers.push_back(std::make_unique<QString>("Hello!"));
                    }
                    

                    or

                    for (int i = 0; i < 10; ++i)
                    {
                        auto ptr = std::make_unique<QString>("Hello!");
                        pointers.push_back(std::move(ptr));
                    
                        // ptr does not point to the object here anymore, it's been moved from.
                        // Code below will compile but is invalid and will likely crash at runtime:
                        ptr->isEmpty();
                    }
                    

                    You can then access these pointers like this:

                    for (int i = 0; i < 10; ++i)
                    {
                        bool use_the_string = pointers.at(i)->isEmpty();
                    }
                    

                    or get a reference to the pointer:

                    for (int i = 0; i < 10; ++i)
                    {
                        const auto& ptr_ref = pointers.at(i);
                        bool use_the_string = ptr_ref->isEmpty();
                    }
                    

                    but you can't copy them:

                    for (int i = 0; i < 10; ++i)
                    {
                       // This won't compile. You can't copy unique_ptrs
                        auto ptr_copy = pointers.at(i);
                    }
                    

                    If you want to copy the object (not the pointer!) you can do it like this:

                    for (int i = 0; i < 10; ++i)
                    {
                        QString string_copy = *pointers.at(i);
                    }
                    
                    mzimmersM 1 Reply Last reply
                    3
                    • mzimmersM mzimmers

                      @Christian-Ehrlicher

                      sigh I new I should have taken that extra C++ class in night school...

                      OK, so I think I've added the move c'tor and move operator (though the docs are a little confusing; there's reference to a move assignment operator, which I'm not sure is the same thing). Code looks like this:

                      struct TestStruct {
                      	int i;
                      	TestStruct() {}
                      	TestStruct(TestStruct &ts) {i = ts.i;} // copy c'tor
                      	TestStruct(TestStruct &&ts) { i = ts.i; } // move c'tor
                      	TestStruct& operator=(TestStruct&& ts) { return *this; } // move operator
                      };
                      TestStruct myStruct;
                      
                      std::unique_ptr<TestStruct> *uniquePtr = nullptr;
                      QList<std::unique_ptr<TestStruct>> list;
                      uniquePtr = &myStruct; // how to form this?
                      list.push_back(*uniquePtr); // how to form this?
                      

                      What am I continuing to do wrong here?

                      Thanks...

                      JoeCFDJ Offline
                      JoeCFDJ Offline
                      JoeCFD
                      wrote on last edited by JoeCFD
                      #20

                      @mzimmers said in trying to understand smart pointers...:

                      uniquePtr = &myStruct; // how to form this?

                      This is not allowed. myStruct sits in stack and will be cleared when your app runs out of its scope. However, uniquePtr will destroy it again when uniquePtr is not used anymore.

                      1 Reply Last reply
                      0
                      • Chris KawaC Chris Kawa

                        @mzimmers said:

                        I'll be making temporary variables of my struct, and copying to and from the list.

                        The whole point of unique_ptr is that it is unique. It holds ownership of the object. You can't copy (temporary or otherwise) unique_ptr because then you would have two things owning the same object and that would just crash because of double delete.

                        You can move unique_ptrs, because it moves ownership of the object, so only one pointer still owns the object.

                        As others mentioned QList does not support move-only types because of implicit sharing. It needs to do copies underneath when a shared data detaches.

                        QScopedPointer is just a simplified version of std::unique_ptr. Switching one to the other doesn't change anything.

                        You can store unique_ptr in a std::vector, which does not do implicit sharing and supports move-only types:

                        std::vector<std::unique_ptr<QString>> pointers;
                        for (int i=0; i < 10; ++i)
                        {
                            pointers.push_back(std::make_unique<QString>("Hello!"));
                        }
                        

                        or

                        for (int i = 0; i < 10; ++i)
                        {
                            auto ptr = std::make_unique<QString>("Hello!");
                            pointers.push_back(std::move(ptr));
                        
                            // ptr does not point to the object here anymore, it's been moved from.
                            // Code below will compile but is invalid and will likely crash at runtime:
                            ptr->isEmpty();
                        }
                        

                        You can then access these pointers like this:

                        for (int i = 0; i < 10; ++i)
                        {
                            bool use_the_string = pointers.at(i)->isEmpty();
                        }
                        

                        or get a reference to the pointer:

                        for (int i = 0; i < 10; ++i)
                        {
                            const auto& ptr_ref = pointers.at(i);
                            bool use_the_string = ptr_ref->isEmpty();
                        }
                        

                        but you can't copy them:

                        for (int i = 0; i < 10; ++i)
                        {
                           // This won't compile. You can't copy unique_ptrs
                            auto ptr_copy = pointers.at(i);
                        }
                        

                        If you want to copy the object (not the pointer!) you can do it like this:

                        for (int i = 0; i < 10; ++i)
                        {
                            QString string_copy = *pointers.at(i);
                        }
                        
                        mzimmersM Offline
                        mzimmersM Offline
                        mzimmers
                        wrote on last edited by
                        #21

                        @Chris-Kawa thanks for the detailed explanation. So, since I can't copy a unique_ptr, do I just copy the contents? Here's what I'd like to do (but I know it won't work):

                        std::unique_ptr<Equipment> pEquipment;
                        
                        listIndex = getIndex();
                        if (listIndex == NOT_IN_LIST) { // create a new one.
                        	pEquipment = new Equipment();
                        } else {
                        	pEquipment = (&m_list->at(listIndex)); // point to element in list
                        }
                        

                        This code is in a function that updates my list, either by ultimately modifying an existing element, or adding a new element. When this was just a list of objects (not pointers), I was trying to use a pointer to do double-duty, in order to reduce unnecessary constructor calls.

                        JoeCFDJ Chris KawaC 2 Replies Last reply
                        0
                        • mzimmersM mzimmers

                          @Chris-Kawa thanks for the detailed explanation. So, since I can't copy a unique_ptr, do I just copy the contents? Here's what I'd like to do (but I know it won't work):

                          std::unique_ptr<Equipment> pEquipment;
                          
                          listIndex = getIndex();
                          if (listIndex == NOT_IN_LIST) { // create a new one.
                          	pEquipment = new Equipment();
                          } else {
                          	pEquipment = (&m_list->at(listIndex)); // point to element in list
                          }
                          

                          This code is in a function that updates my list, either by ultimately modifying an existing element, or adding a new element. When this was just a list of objects (not pointers), I was trying to use a pointer to do double-duty, in order to reduce unnecessary constructor calls.

                          JoeCFDJ Offline
                          JoeCFDJ Offline
                          JoeCFD
                          wrote on last edited by JoeCFD
                          #22

                          @mzimmers why not std::shared_ptr? A shared pointer does not trigger extra construtor call if it is not dereferenced.

                          The following code may crash. You may never try to assign a stack memory to a shared or unique pointer.

                          pEquipment = (&m_list->at(listIndex)); // point to element in list
                          

                          Also check std::weak_ptr which does not have ownership of the pointer.

                          1 Reply Last reply
                          0
                          • mzimmersM mzimmers

                            @Chris-Kawa thanks for the detailed explanation. So, since I can't copy a unique_ptr, do I just copy the contents? Here's what I'd like to do (but I know it won't work):

                            std::unique_ptr<Equipment> pEquipment;
                            
                            listIndex = getIndex();
                            if (listIndex == NOT_IN_LIST) { // create a new one.
                            	pEquipment = new Equipment();
                            } else {
                            	pEquipment = (&m_list->at(listIndex)); // point to element in list
                            }
                            

                            This code is in a function that updates my list, either by ultimately modifying an existing element, or adding a new element. When this was just a list of objects (not pointers), I was trying to use a pointer to do double-duty, in order to reduce unnecessary constructor calls.

                            Chris KawaC Offline
                            Chris KawaC Offline
                            Chris Kawa
                            Lifetime Qt Champion
                            wrote on last edited by
                            #23

                            @mzimmers Smart pointers are not just fancy pants replacements for regular pointers. They are means to reason about ownership.
                            When using smart pointers you have to think about who's the owner of the object and who just wants to access it.

                            Consider this would compile (it doesn't but bare with me):

                            pEquipment = (&m_list->at(listIndex));
                            

                            Who owns the object here, and by owns I mean who is responsible for deleting it? m_list or pEquipment? If they both tried to delete the same object your app would crash.

                            unique_ptr owns the object. You can move the object to another unique_ptr, but you can't make a copy of the pointer.

                            So to answer what you should do first figure out what you want to achieve in terms of ownership.

                            If you want to have a single point of ownership use unique_ptr. You can transfer (move) the ownership to another unique_ptr, but only one of them at a time can own the object.

                            If you want to have multiple points of ownership, in the style of "last leaving the room turns off the light" then you use shared_ptr. All copies of shared_ptr own the object and the last one that is destroyed deletes the object.

                            If you want to have a single point of ownership and just get access to it sporadically without changing the ownership use unique_ptr to own the object and unique_ptr::get() to get a raw pointer to the managed object.

                            If you want shared ownership and means to monitor when the object lives and dies use shared_ptr to hold ownership and weak_ptr to get a non-owning pointer that gets nulled when the object is destroyed by last existing shared_ptr

                            mzimmersM 1 Reply Last reply
                            1
                            • Chris KawaC Chris Kawa

                              @mzimmers Smart pointers are not just fancy pants replacements for regular pointers. They are means to reason about ownership.
                              When using smart pointers you have to think about who's the owner of the object and who just wants to access it.

                              Consider this would compile (it doesn't but bare with me):

                              pEquipment = (&m_list->at(listIndex));
                              

                              Who owns the object here, and by owns I mean who is responsible for deleting it? m_list or pEquipment? If they both tried to delete the same object your app would crash.

                              unique_ptr owns the object. You can move the object to another unique_ptr, but you can't make a copy of the pointer.

                              So to answer what you should do first figure out what you want to achieve in terms of ownership.

                              If you want to have a single point of ownership use unique_ptr. You can transfer (move) the ownership to another unique_ptr, but only one of them at a time can own the object.

                              If you want to have multiple points of ownership, in the style of "last leaving the room turns off the light" then you use shared_ptr. All copies of shared_ptr own the object and the last one that is destroyed deletes the object.

                              If you want to have a single point of ownership and just get access to it sporadically without changing the ownership use unique_ptr to own the object and unique_ptr::get() to get a raw pointer to the managed object.

                              If you want shared ownership and means to monitor when the object lives and dies use shared_ptr to hold ownership and weak_ptr to get a non-owning pointer that gets nulled when the object is destroyed by last existing shared_ptr

                              mzimmersM Offline
                              mzimmersM Offline
                              mzimmers
                              wrote on last edited by mzimmers
                              #24

                              @Chris-Kawa I have to admit that ownership wasn't a factor (in my mind) when I began this.

                              To start from the beginning:

                              1. I have a Qt model that contains a list (now probably a std::vector) of a struct that I've defined.
                              2. this struct will have many subclasses, but I want to keep a single list in my model.
                              3. I was told that the way to accomplish #2 was to keep a list of pointers instead of a list of the actual objects.
                              4. Since I would be using pointers, I figured that I'd look into smart pointers, mostly for the benefit of the automated destructors.
                              5. for convenience and coding clarity, I want to use a pointer to access list members, rather than dereferencing the list. In other words,
                              pEquipment->m_uuid = message.uuid;
                              

                              is preferable to:

                              m_list.at(listIndex)->m_uuid = message.uuid;
                              

                              at least in my mind.

                              But I'm now questioning whether any of the smart pointers are appropriate for me. Since my last post, I've created a shared_ptr member variable in my model, and do stuff like:

                              pEquipment = std::make_shared<Equipment>();
                              

                              Based on what you're telling me, this may not be a great idea. It sounds like your option of the unique_ptr and the get() function may be a better choice, but I still find myself needing to assign values to the pointers.

                              Chris KawaC 1 Reply Last reply
                              0
                              • mzimmersM mzimmers

                                @Chris-Kawa I have to admit that ownership wasn't a factor (in my mind) when I began this.

                                To start from the beginning:

                                1. I have a Qt model that contains a list (now probably a std::vector) of a struct that I've defined.
                                2. this struct will have many subclasses, but I want to keep a single list in my model.
                                3. I was told that the way to accomplish #2 was to keep a list of pointers instead of a list of the actual objects.
                                4. Since I would be using pointers, I figured that I'd look into smart pointers, mostly for the benefit of the automated destructors.
                                5. for convenience and coding clarity, I want to use a pointer to access list members, rather than dereferencing the list. In other words,
                                pEquipment->m_uuid = message.uuid;
                                

                                is preferable to:

                                m_list.at(listIndex)->m_uuid = message.uuid;
                                

                                at least in my mind.

                                But I'm now questioning whether any of the smart pointers are appropriate for me. Since my last post, I've created a shared_ptr member variable in my model, and do stuff like:

                                pEquipment = std::make_shared<Equipment>();
                                

                                Based on what you're telling me, this may not be a great idea. It sounds like your option of the unique_ptr and the get() function may be a better choice, but I still find myself needing to assign values to the pointers.

                                Chris KawaC Offline
                                Chris KawaC Offline
                                Chris Kawa
                                Lifetime Qt Champion
                                wrote on last edited by Chris Kawa
                                #25

                                @mzimmers said:

                                Based on what you're telling me, this may not be a great idea.

                                It's not. shared_ptr is for sharing ownership, not for solving "it doesn't compile otherwise" issues.

                                I have a Qt model that contains a list (now probably a std::vector) of a struct that I've defined.

                                So in terms of ownership you have a container that owns a bunch of objects (releases them when it gets destroyed). Yes, equivalent pointer solution is std::vector of std::unique_ptr.

                                but I still find myself needing to assign values to the pointers

                                unique_ptr does not prevent you from changing the value of the object it points to or replacing the object entirely. You just can't make a copy of the pointer.

                                std::vector<std::unique_ptr<QString>> pointers;
                                
                                // Add a new value
                                pointers.push_back(std::make_unique<QString>("Hello"));
                                
                                // Move an existing value in, str does not point to valid object after move
                                std::unique_ptr<QString> str = std::make_unique<QString>("Hello");
                                pointers.push_back(std::move(str));
                                
                                // Replace existing pointer with a new one, old one gets deleted along with its managed object
                                pointers.at(0) = std::make_unique<QString>("Hello");
                                
                                // Replace object the pointer points to without replacing the pointer itself, old object gets deleted
                                pointers.at(0).reset(new QString("Hello"));
                                
                                // Modify existing object directly
                                pointers.at(0)->append(" World!");
                                
                                // Get a non-owning pointer to object and modify the object
                                QString* str_ptr = pointers.at(0).get();
                                str_ptr->append(" World!");
                                
                                // Get a reference to object and modify the object
                                QString& str_ref = *pointers.at(0);
                                str_ref.append(" World!");
                                
                                mzimmersM 1 Reply Last reply
                                2
                                • Chris KawaC Chris Kawa

                                  @mzimmers said:

                                  Based on what you're telling me, this may not be a great idea.

                                  It's not. shared_ptr is for sharing ownership, not for solving "it doesn't compile otherwise" issues.

                                  I have a Qt model that contains a list (now probably a std::vector) of a struct that I've defined.

                                  So in terms of ownership you have a container that owns a bunch of objects (releases them when it gets destroyed). Yes, equivalent pointer solution is std::vector of std::unique_ptr.

                                  but I still find myself needing to assign values to the pointers

                                  unique_ptr does not prevent you from changing the value of the object it points to or replacing the object entirely. You just can't make a copy of the pointer.

                                  std::vector<std::unique_ptr<QString>> pointers;
                                  
                                  // Add a new value
                                  pointers.push_back(std::make_unique<QString>("Hello"));
                                  
                                  // Move an existing value in, str does not point to valid object after move
                                  std::unique_ptr<QString> str = std::make_unique<QString>("Hello");
                                  pointers.push_back(std::move(str));
                                  
                                  // Replace existing pointer with a new one, old one gets deleted along with its managed object
                                  pointers.at(0) = std::make_unique<QString>("Hello");
                                  
                                  // Replace object the pointer points to without replacing the pointer itself, old object gets deleted
                                  pointers.at(0).reset(new QString("Hello"));
                                  
                                  // Modify existing object directly
                                  pointers.at(0)->append(" World!");
                                  
                                  // Get a non-owning pointer to object and modify the object
                                  QString* str_ptr = pointers.at(0).get();
                                  str_ptr->append(" World!");
                                  
                                  // Get a reference to object and modify the object
                                  QString& str_ref = *pointers.at(0);
                                  str_ref.append(" World!");
                                  
                                  mzimmersM Offline
                                  mzimmersM Offline
                                  mzimmers
                                  wrote on last edited by mzimmers
                                  #26

                                  @Chris-Kawa @JoeCFD so...perhaps I don't need smart pointers after all - I was only interested in the garbage collection, and even that is a minor issue, given how rarely this list is going to change (though the elements themselves might).

                                  This has been a good education; I appreciate all the assistance. Before I close the topic, I'd like to ask a final question: how would I do this with a unique_ptr?

                                  pEquipment = std::make_shared<Equipment>();
                                  insertRows(m_list->size(), 1);
                                  listIndex = m_list->size() - 1; // point to last element
                                  pEquipment = m_list->at(listIndex);
                                  

                                  where insertRows() overrides a function of QAbstractItemModel, and the list insertion must take place here.

                                  Chris KawaC 1 Reply Last reply
                                  0
                                  • mzimmersM mzimmers

                                    @Chris-Kawa @JoeCFD so...perhaps I don't need smart pointers after all - I was only interested in the garbage collection, and even that is a minor issue, given how rarely this list is going to change (though the elements themselves might).

                                    This has been a good education; I appreciate all the assistance. Before I close the topic, I'd like to ask a final question: how would I do this with a unique_ptr?

                                    pEquipment = std::make_shared<Equipment>();
                                    insertRows(m_list->size(), 1);
                                    listIndex = m_list->size() - 1; // point to last element
                                    pEquipment = m_list->at(listIndex);
                                    

                                    where insertRows() overrides a function of QAbstractItemModel, and the list insertion must take place here.

                                    Chris KawaC Offline
                                    Chris KawaC Offline
                                    Chris Kawa
                                    Lifetime Qt Champion
                                    wrote on last edited by
                                    #27

                                    @mzimmers There's no garbage collection involved in smart pointers. Garbage collection is a different model of memory management.

                                    how would I do this with a unique_ptr?

                                    If m_list is you QList of structs then this code makes no sense. I'm not sure what you want to do here.

                                    pEquipment = m_list->at(listIndex);
                                    What is the intention here? pEquipment is an owning pointer. m_list owns some objects, at() returns a reference to an object, so you're assigning an object reference to an owning pointer?

                                    if m_list is vector of unique_ptr then it also makes no sense. pEquipment is an owning pointer, so it takes ownership of anything you assign to it. You can't have two unique_ptrs (one in the container and another as a variable) pointing to the same object. Only one unique_ptr can own an object. If you want to transfer the ownership from one to the other you would do it like this:

                                    pEquipment = m_list->at(listIndex).release();
                                    

                                    but that essentially takes the object out of the list and puts it into pEquipment.

                                    Again - don't think about unique_ptr as a replacement of raw pointers. Raw pointers don't own anything so you can copy them all day long:

                                    int* a = new int(42);
                                    int* b = a; //both point to the same thing now
                                    

                                    You can't do that with unique_ptr. You can only transfer the ownership from one pointer to the other, but not copy it.

                                    unique_ptr<int> a = make_unique<int>(42);
                                    unique_ptr<int> b = a; // this won't compile, can't copy
                                    unique_ptr<int> c = a.release(); // moves object from a to c, a is now empty
                                    
                                    mzimmersM 1 Reply Last reply
                                    2
                                    • Chris KawaC Chris Kawa

                                      @mzimmers There's no garbage collection involved in smart pointers. Garbage collection is a different model of memory management.

                                      how would I do this with a unique_ptr?

                                      If m_list is you QList of structs then this code makes no sense. I'm not sure what you want to do here.

                                      pEquipment = m_list->at(listIndex);
                                      What is the intention here? pEquipment is an owning pointer. m_list owns some objects, at() returns a reference to an object, so you're assigning an object reference to an owning pointer?

                                      if m_list is vector of unique_ptr then it also makes no sense. pEquipment is an owning pointer, so it takes ownership of anything you assign to it. You can't have two unique_ptrs (one in the container and another as a variable) pointing to the same object. Only one unique_ptr can own an object. If you want to transfer the ownership from one to the other you would do it like this:

                                      pEquipment = m_list->at(listIndex).release();
                                      

                                      but that essentially takes the object out of the list and puts it into pEquipment.

                                      Again - don't think about unique_ptr as a replacement of raw pointers. Raw pointers don't own anything so you can copy them all day long:

                                      int* a = new int(42);
                                      int* b = a; //both point to the same thing now
                                      

                                      You can't do that with unique_ptr. You can only transfer the ownership from one pointer to the other, but not copy it.

                                      unique_ptr<int> a = make_unique<int>(42);
                                      unique_ptr<int> b = a; // this won't compile, can't copy
                                      unique_ptr<int> c = a.release(); // moves object from a to c, a is now empty
                                      
                                      mzimmersM Offline
                                      mzimmersM Offline
                                      mzimmers
                                      wrote on last edited by
                                      #28

                                      @Chris-Kawa said in trying to understand smart pointers...:

                                      What is the intention here?

                                      Simply to have a more convenient/readable way of referencing (for read and write) the items in the list. No ownership transfer is desired.

                                      I can do this with shared_ptrs; I just had the impression that you didn't care much for it, so I was looking for alternatives.

                                      Chris KawaC 1 Reply Last reply
                                      0
                                      • mzimmersM mzimmers

                                        @Chris-Kawa said in trying to understand smart pointers...:

                                        What is the intention here?

                                        Simply to have a more convenient/readable way of referencing (for read and write) the items in the list. No ownership transfer is desired.

                                        I can do this with shared_ptrs; I just had the impression that you didn't care much for it, so I was looking for alternatives.

                                        Chris KawaC Offline
                                        Chris KawaC Offline
                                        Chris Kawa
                                        Lifetime Qt Champion
                                        wrote on last edited by Chris Kawa
                                        #29

                                        @mzimmers said:

                                        No ownership transfer is desired.

                                        Then don't use unique_ptr or shared_ptr for pEquipment. If it's non-owning use a non owning pointer, like in my previous example:

                                        std::vector<std::unique_ptr<Equipment>> m_list;
                                        m_list.push_back(std::make_unique<Equipment>()); // m_list.at(0) owns the object
                                        Equipment* pEquipment = m_list.at(0).get(); // pEquipment does not own the object, just points to it
                                        mzimmersM 1 Reply Last reply
                                        2
                                        • Chris KawaC Chris Kawa

                                          @mzimmers said:

                                          No ownership transfer is desired.

                                          Then don't use unique_ptr or shared_ptr for pEquipment. If it's non-owning use a non owning pointer, like in my previous example:

                                          std::vector<std::unique_ptr<Equipment>> m_list;
                                          m_list.push_back(std::make_unique<Equipment>()); // m_list.at(0) owns the object
                                          Equipment* pEquipment = m_list.at(0).get(); // pEquipment does not own the object, just points to it
                                          mzimmersM Offline
                                          mzimmersM Offline
                                          mzimmers
                                          wrote on last edited by
                                          #30

                                          @Chris-Kawa if I make pEquipment local to this function, then I can't use it in my insertRows() call. That's why I made it a member of the class. I can still use your technique of accessing list elements with a conventional pointer, though...thoughts?

                                          Based on what I've shown about my app, should I even be using unique_ptrs or shared_ptrs in my list/vector?

                                          1 Reply Last reply
                                          0
                                          • mzimmersM mzimmers has marked this topic as solved on

                                          • Login

                                          • Login or register to search.
                                          • First post
                                            Last post
                                          0
                                          • Categories
                                          • Recent
                                          • Tags
                                          • Popular
                                          • Users
                                          • Groups
                                          • Search
                                          • Get Qt Extensions
                                          • Unsolved