Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Some small questions about qt container detachment.
Qt 6.11 is out! See what's new in the release blog

Some small questions about qt container detachment.

Scheduled Pinned Locked Moved Solved General and Desktop
15 Posts 3 Posters 3.4k Views 1 Watching
  • 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.
  • P Offline
    P Offline
    PikaCat
    wrote on last edited by PikaCat
    #1

    This code will corrupt immediately:

      QList<QString> test { "a", "b", "c" };
      for (QString &c : test) {
        test.emplaceBack("hello");
        c = "c";
      }
      qDebug() << test;
    

    But this code will run normally:

      QList<QString> test { "a", "b", "c" };
      for (QString &c : test) {
        c = "c";
      }
      qDebug() << test;
    

    Question 1:
    After detaching from the "test" container. Why I'm still able to access the deleted shared data? (see the second code snippet)

    Question 2:
    After detaching, is that shared data becomes READ-ONLY instead of deleting immediately? (see the first code snippet)

    Question 3:
    Why the code below does not crash:

      QSet<QString> test { "hello", "world" };
      for (const QString &str : test) {
        qDebug() << str;
        test.insert("yes");
      }
      qDebug() << test;
    

    if you call insert on "test", it will detach, and the old shared data ["hello", "world"] will be deleted and qDebug() << str will be accessing deleted thing and it should crash.

    Also this one:

      QList<QString> test { "hello", "world" };
      for (const QString &str : test) {
        qDebug() << str;
        test.emplaceBack("he");
      }
      qDebug() << test;
    

    We magically do not crash by accessing deleted list share data.

    Could someone please save me from this annoying qt detachment and explain qt detachment in a more human-readable way (compare to the official qt documentation)?

    KroMignonK 1 Reply Last reply
    0
    • P PikaCat

      This code will corrupt immediately:

        QList<QString> test { "a", "b", "c" };
        for (QString &c : test) {
          test.emplaceBack("hello");
          c = "c";
        }
        qDebug() << test;
      

      But this code will run normally:

        QList<QString> test { "a", "b", "c" };
        for (QString &c : test) {
          c = "c";
        }
        qDebug() << test;
      

      Question 1:
      After detaching from the "test" container. Why I'm still able to access the deleted shared data? (see the second code snippet)

      Question 2:
      After detaching, is that shared data becomes READ-ONLY instead of deleting immediately? (see the first code snippet)

      Question 3:
      Why the code below does not crash:

        QSet<QString> test { "hello", "world" };
        for (const QString &str : test) {
          qDebug() << str;
          test.insert("yes");
        }
        qDebug() << test;
      

      if you call insert on "test", it will detach, and the old shared data ["hello", "world"] will be deleted and qDebug() << str will be accessing deleted thing and it should crash.

      Also this one:

        QList<QString> test { "hello", "world" };
        for (const QString &str : test) {
          qDebug() << str;
          test.emplaceBack("he");
        }
        qDebug() << test;
      

      We magically do not crash by accessing deleted list share data.

      Could someone please save me from this annoying qt detachment and explain qt detachment in a more human-readable way (compare to the official qt documentation)?

      KroMignonK Offline
      KroMignonK Offline
      KroMignon
      wrote on last edited by
      #2

      @PikaCat

      QString is a container class which use implicit sharing, which means when you are creating copies of a QString instance, the contained string will not be copied but only a counter is incremented.
      Only when the instance content is changed, the instance will be detached, which means the counter is decremented and a buffer is allocated to store the new string.
      This way you can save memory and avoid unneeded copies, which will also speed up the application.

      In first code, in the for loop the QList is modified, so the list reference will be detached, which means a new QList is created.

      In the second, the c is detached as soon as you are modifying it.

      For more details, please take time to read this ==> https://doc.qt.io/qt-5/implicit-sharing.html

      It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

      1 Reply Last reply
      1
      • P Offline
        P Offline
        PikaCat
        wrote on last edited by
        #3

        Thanks and what about question 3?

        KroMignonK 1 Reply Last reply
        0
        • P PikaCat

          Thanks and what about question 3?

          KroMignonK Offline
          KroMignonK Offline
          KroMignon
          wrote on last edited by
          #4

          @PikaCat said in Some small questions about qt container detachment.:

          Thanks and what about question 3?

          Why should it crash?

          Code 1 will crash because you are doing things wrong.
          This code don't make sense.

          It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

          P 2 Replies Last reply
          0
          • KroMignonK KroMignon

            @PikaCat said in Some small questions about qt container detachment.:

            Thanks and what about question 3?

            Why should it crash?

            Code 1 will crash because you are doing things wrong.
            This code don't make sense.

            P Offline
            P Offline
            PikaCat
            wrote on last edited by
            #5

            @KroMignon Because once the container is detached by emplacing a new object, accessing qDebug() << "world" on the next iteration is accessing deleted data.

            1 Reply Last reply
            0
            • KroMignonK KroMignon

              @PikaCat said in Some small questions about qt container detachment.:

              Thanks and what about question 3?

              Why should it crash?

              Code 1 will crash because you are doing things wrong.
              This code don't make sense.

              P Offline
              P Offline
              PikaCat
              wrote on last edited by
              #6

              @KroMignon the code also works normally if you changes the code order sightly:

                QList<QString> test { "hello", "world" };
                for (const QString &str : test) {
                  test.emplaceBack("he");
                  qDebug() << str;
                }
                qDebug() << test;
              

              And it's apparently wrong because after detaching, the old container is deleted. So the QString object does not exist in the old container and you still want to get a reference of it and output the content of it.

              KroMignonK 1 Reply Last reply
              0
              • P PikaCat

                @KroMignon the code also works normally if you changes the code order sightly:

                  QList<QString> test { "hello", "world" };
                  for (const QString &str : test) {
                    test.emplaceBack("he");
                    qDebug() << str;
                  }
                  qDebug() << test;
                

                And it's apparently wrong because after detaching, the old container is deleted. So the QString object does not exist in the old container and you still want to get a reference of it and output the content of it.

                KroMignonK Offline
                KroMignonK Offline
                KroMignon
                wrote on last edited by KroMignon
                #7

                @PikaCat said in Some small questions about qt container detachment.:

                And it's apparently wrong because after detaching, the old container is deleted. So the QString object does not exist in the old container and you still want to get a reference of it and output the content of it.

                You are responsible of your code, so you have to be consistant.
                If you want to change a container instance during iteration, you have to use an appropriated way to iterate:

                for(int idx = 0; idx < test.length(); ++idx)
                {
                }
                

                or

                QMutableListIterator<QString> iter(test);
                while(iter.hasNext())
                {
                    iter.next();
                }
                

                If you are written mess code, then you can not expect it will work.

                It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

                P 1 Reply Last reply
                0
                • KroMignonK KroMignon

                  @PikaCat said in Some small questions about qt container detachment.:

                  And it's apparently wrong because after detaching, the old container is deleted. So the QString object does not exist in the old container and you still want to get a reference of it and output the content of it.

                  You are responsible of your code, so you have to be consistant.
                  If you want to change a container instance during iteration, you have to use an appropriated way to iterate:

                  for(int idx = 0; idx < test.length(); ++idx)
                  {
                  }
                  

                  or

                  QMutableListIterator<QString> iter(test);
                  while(iter.hasNext())
                  {
                      iter.next();
                  }
                  

                  If you are written mess code, then you can not expect it will work.

                  P Offline
                  P Offline
                  PikaCat
                  wrote on last edited by PikaCat
                  #8

                  @KroMignon I know that code should be like that if you want to modify the container during iteration. But my orginal question is : Why accessing like I did above will not make the program crash? I just don't understand why we can access a memory that is deleted.
                  It just magically feels like you apply a deep copy to the container and use the copied one to do the C++11 range loop. In that scenario, modifying the original one won't affect the loop.
                  But qt's documentation said that this will only happen if you are using qt foreach loop. In range loop, it will not happen. And if that's the case, how begin() and end() iterator in the C++11 range loop works?
                  see https://doc.qt.io/qt-6/foreach-keyword.html

                  KroMignonK 1 Reply Last reply
                  0
                  • P PikaCat

                    @KroMignon I know that code should be like that if you want to modify the container during iteration. But my orginal question is : Why accessing like I did above will not make the program crash? I just don't understand why we can access a memory that is deleted.
                    It just magically feels like you apply a deep copy to the container and use the copied one to do the C++11 range loop. In that scenario, modifying the original one won't affect the loop.
                    But qt's documentation said that this will only happen if you are using qt foreach loop. In range loop, it will not happen. And if that's the case, how begin() and end() iterator in the C++11 range loop works?
                    see https://doc.qt.io/qt-6/foreach-keyword.html

                    KroMignonK Offline
                    KroMignonK Offline
                    KroMignon
                    wrote on last edited by
                    #9

                    @PikaCat said in Some small questions about qt container detachment.:

                    But my orginal question is : Why accessing like I did above will not make the program crash?

                    Because you are lucky ;)
                    This code crashes with Qt 5.12 and MSVC2015 in release for example, but not in debug build.

                    It may crash, but it may work depending on operating system and compiler options.

                    It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

                    P 1 Reply Last reply
                    1
                    • KroMignonK KroMignon

                      @PikaCat said in Some small questions about qt container detachment.:

                      But my orginal question is : Why accessing like I did above will not make the program crash?

                      Because you are lucky ;)
                      This code crashes with Qt 5.12 and MSVC2015 in release for example, but not in debug build.

                      It may crash, but it may work depending on operating system and compiler options.

                      P Offline
                      P Offline
                      PikaCat
                      wrote on last edited by PikaCat
                      #10

                      @KroMignon Thanks. I finally understand the crash. One last question: After detaching, in the C++11 range loop, does the test.end() returns the new container's end iterator or the old one? Theoretically it should returns the new one. But why when it is not crash, it can still tell where is the end of this iteration by using the begin iterator of the old container and the end iterator of the new container. Which is not a normal iterator pair!

                      KroMignonK artwawA 2 Replies Last reply
                      0
                      • P PikaCat

                        @KroMignon Thanks. I finally understand the crash. One last question: After detaching, in the C++11 range loop, does the test.end() returns the new container's end iterator or the old one? Theoretically it should returns the new one. But why when it is not crash, it can still tell where is the end of this iteration by using the begin iterator of the old container and the end iterator of the new container. Which is not a normal iterator pair!

                        KroMignonK Offline
                        KroMignonK Offline
                        KroMignon
                        wrote on last edited by
                        #11

                        @PikaCat said in Some small questions about qt container detachment.:

                        After detaching, Does the test.end() returns the new container's end iterator or the old one? Theoretically it should returns the new one. But why when it is not crash, it can still tell where is the end of this iteration by using the begin iterator of the old container and the end iterator of the new container. Which is not a normal iterator pair!

                        Don't understand the question :(
                        test.end() always returns current iterator end, problem is the detached copy which could point to an item which no more exits.

                        It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

                        P 2 Replies Last reply
                        0
                        • P PikaCat

                          @KroMignon Thanks. I finally understand the crash. One last question: After detaching, in the C++11 range loop, does the test.end() returns the new container's end iterator or the old one? Theoretically it should returns the new one. But why when it is not crash, it can still tell where is the end of this iteration by using the begin iterator of the old container and the end iterator of the new container. Which is not a normal iterator pair!

                          artwawA Offline
                          artwawA Offline
                          artwaw
                          wrote on last edited by
                          #12

                          @PikaCat Please also take a look at this article https://marcmutz.wordpress.com/effective-qt/containers/

                          It is a bit dated, some things have changed (QList is now performs as well as QVector if I understood the changes correctly) but it still gives a nice overview.

                          For more information please re-read.

                          Kind Regards,
                          Artur

                          1 Reply Last reply
                          0
                          • KroMignonK KroMignon

                            @PikaCat said in Some small questions about qt container detachment.:

                            After detaching, Does the test.end() returns the new container's end iterator or the old one? Theoretically it should returns the new one. But why when it is not crash, it can still tell where is the end of this iteration by using the begin iterator of the old container and the end iterator of the new container. Which is not a normal iterator pair!

                            Don't understand the question :(
                            test.end() always returns current iterator end, problem is the detached copy which could point to an item which no more exits.

                            P Offline
                            P Offline
                            PikaCat
                            wrote on last edited by PikaCat
                            #13

                            @KroMignon Because C++11 range loop is implemented using iterators, So for(auto &a : b) is equivalent to for(auto iter = b.begin(): iter != b.end(): ++iter) auto&a = *iter;
                            After detaching, iter is the iterator of the old container, b.end() is end iterator of the newly created container. So how is it possible to loop like that when it is not crash.

                            1 Reply Last reply
                            0
                            • KroMignonK KroMignon

                              @PikaCat said in Some small questions about qt container detachment.:

                              After detaching, Does the test.end() returns the new container's end iterator or the old one? Theoretically it should returns the new one. But why when it is not crash, it can still tell where is the end of this iteration by using the begin iterator of the old container and the end iterator of the new container. Which is not a normal iterator pair!

                              Don't understand the question :(
                              test.end() always returns current iterator end, problem is the detached copy which could point to an item which no more exits.

                              P Offline
                              P Offline
                              PikaCat
                              wrote on last edited by PikaCat
                              #14

                              @KroMignon Well, I found the answer on stackoverflow https://stackoverflow.com/questions/29578219/range-based-for-loop-vs-regular-iterator-for-loop.
                              end() is evaluated only once in the begining of range for, not every time. So for(auto &a : b) is actually equivalent to for(auto iter = b.begin(), endIter = b.end();iter !=endIter; ++iter) auto&a = *iter;
                              That perfectly explains everything.
                              Thanks anyway:) appreciate that.

                              KroMignonK 1 Reply Last reply
                              0
                              • P PikaCat

                                @KroMignon Well, I found the answer on stackoverflow https://stackoverflow.com/questions/29578219/range-based-for-loop-vs-regular-iterator-for-loop.
                                end() is evaluated only once in the begining of range for, not every time. So for(auto &a : b) is actually equivalent to for(auto iter = b.begin(), endIter = b.end();iter !=endIter; ++iter) auto&a = *iter;
                                That perfectly explains everything.
                                Thanks anyway:) appreciate that.

                                KroMignonK Offline
                                KroMignonK Offline
                                KroMignon
                                wrote on last edited by
                                #15

                                @PikaCat said in Some small questions about qt container detachment.:

                                I found the answer on stackoverflow https://stackoverflow.com/questions/29578219/range-based-for-loop-vs-regular-iterator-for-loop.
                                end() is evaluated only once in the begining of range for

                                Sorry, for me it was obvious :(

                                Thanks anyway:) appreciate that.

                                Your welcome.

                                It is an old maxim of mine that when you have excluded the impossible, whatever remains, however improbable, must be the truth. (Sherlock Holmes)

                                1 Reply Last reply
                                0

                                • Login

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