Another talk on Qvector and Qbytearray--qt's write-time copy (copy on write) technology

Source: Internet
Author: User



Qt as an excellent cross-platform open-source C + + framework, if we only stay on the basis of using it and not deep digging its implementation, it is a waste of this knowledge of the Treasury ~ our previous blog qvector memory allocation strategy and Talk about Qvector and std::vector--using decorators to let std::vector support continuous assignment in a simple conversation about qvector memory allocation and assignment, and today then from Qvector to talk about Qt's write-time replication technology. to be honest, "implicit sharing, reference counting, copy-on-write" is also a cliché topic, but also one of the biggest differences between QTL and STL, this blog does not dwell on the "copy-on-write" technical details, that there are a lot of articles introduced, we pull some about operator[] and Qbyteref and Qstringref's Duzi is good.






Needless to say, directly into the topic, we start with a section of the most common code:




QVector <int> v1;
     v1 << 1 << 2 << 3 << 4 << 5;
     QVector <int> v2 (v1); // At this time v2 and v1 share data (memory)
     v2 [1] = 8; // write operation occurs, v2 is allocated new memory
     qDebug () << v1 << endl << v2;


with "copy-on-write" technology, v1 "copy" to V2 is not immediately copied, V2 just points to V1 memory address, only when the V2 is modified to really allocate new memory. This avoids unnecessary memory waste and constructs the cost of destruction. thanks to Qt's good encapsulation, even if we don't know all this, we still enjoy the benefits of the "copy-on-write" mechanism. But if you do not understand the underlying, and inadvertently write down this code, You may be curious about the output of the results :




QVector <int> v1;
     v1 << 1 << 2 << 3 << 4 << 5;
     int * p = & v1 [1]; // declare a pointer to the second data of v1
     QVector <int> v2 (v1); // At this time v2 and v1 share data (memory)
     * p = 8; // use pointer to modify v1 data
     qDebug () << v1 << endl << v2;

as you can see, we modify the V1 with pointers to V1, and the results V1 and v2 data are changed. The reason is that the write behavior of "modifying memory values with pointers" cannot be detected by the Qvector class, and thus cannot trigger its replication mechanism, and the QT code should be very careful with this problem.











Is there a way out? Obviously we only have to start with overloading operator[], but this operator is special, like in





int i = v2[1];

as shown in the case, as a read-only situation, we do not need to make V2 independent, so no replication is required.





In this case, as in the case of writing, the content in V2 is about to be modified, so it is necessary to copy out a separate piece of data immediately.




V2[1] = 10;

there is a situation as shown above, V1[1] is positioned by the pointer, we simply can not determine whether the user will modify it after taking it, if not modified and we do in the operator[] copy work is not a waste, if modified, we did not do copy work at that time, Then there is no chance, just like the example we saw above.








Unfortunately, C + + does not distinguish between the [] character is called in the above case, copy may be wasted, all do not copy and will be problematic, how to do it, Qbytearray gave us the answer (qstring similar, as to Qvector, etc. did not adopt this method of reasons, described later).



We know that the call to Qbytearray [] returns a char, so you can write a similar code to see:




    QByteArray str1("HelloWorld");
    char *c = &str1[2];
    QByteArray str2(str1);
    *c = ‘M‘;
    qDebug() << str1 << " " << str2;

We also define a char* pointer, but this time the error is:





"Cannot convert ' qbyteref* ' to ' char* ' Initializaion" (QString is Qstringref)



It seems that the type does not match, but whatever he is, we change the second line so that it matches:




Qbyteref *c = &str1[2];

or there is an error:"taking address of temporary"





The error literal explanation is clear, we are trying to acquire this thing, before the return of the release, of course, can not be quoted. So for this return object, we need to let it produce a copy behavior, which can only be taken:




    QByteArray str1("HelloWorld");
    QByteRef c = str1[2];
    QByteArray str2(str1);
    c = ‘M‘;
    qDebug() << str1 << " " << str2;

Check the print results, no problem, only STR1 has been modified.











When we think about what qbyteref is, we look back and think about the previous question: although we are not sure if operator [] is called in the case of lvalue or rvalue, we can let this function return a proxy class, Then wait to see how the proxy class is being used-if it is read, we treat the invocation of operator[] as a read action, and if it is written, we treat the invocation of operator[] as a write action to perform the copy behavior.



The code for overloading operator[] in Qbytearray is as follows, and nothing is done except to return a Qbyteref object:




inline QByteRef QByteArray::operator[](int i)
{ Q_ASSERT(i >= 0); return QByteRef(*this, i); }
inline QByteRef QByteArray::operator[](uint i)
{ return QByteRef(*this, i); }

Qbyteref is less than 20 lines as an inline class for Qbytearray:






class Q_CORE_EXPORT QByteRef {
    QByteArray &a;
    int i;
    inline QByteRef(QByteArray &array, int idx)
        : a(array),i(idx) {}
    friend class QByteArray;
public:
    inline operator char() const
        { return i < a.d->size ? a.d->data()[i] : char(0); }
    inline QByteRef &operator=(char c)
        { if (i >= a.d->size) a.expand(i); else a.detach();
          a.d->data()[i] = c;  return *this; }
    inline QByteRef &operator=(const QByteRef &c)
        { if (i >= a.d->size) a.expand(i); else a.detach();
          a.d->data()[i] = c.a.d->data()[c.i];  return *this; }
    inline bool operator==(char c) const
    { return a.d->data()[i] == c; }
    inline bool operator!=(char c) const
    { return a.d->data()[i] != c; }
    inline bool operator>(char c) const
    { return a.d->data()[i] > c; }
    inline bool operator>=(char c) const
    { return a.d->data()[i] >= c; }
    inline bool operator<(char c) const
    { return a.d->data()[i] < c; }
    inline bool operator<=(char c) const
    { return a.d->data()[i] <= c; }
};

when Qbyteref is used as the right value (read), operator char () is called, Qbyteref is implicitly converted to char, and the internal data is fetched directly; when operator = is called, it means that qbyteref is now the Lvalue (written). Therefore, the detach () function is called to detach and write the shared memory.








Now we can easily distinguish operator [] of the left and right value of the use, but of course this is also some of the drawbacks, originally we use v1[1] way out of the original data type, such as qvector<int> we can use +,-, + + for v1[1], --, and so on, if qvector<myclass> can also invoke our own defined member function. But once we start using the proxy class, if you disagree, the compiler will not let + +,--, <,> and other things on qxxref type. If we still want to use it the same way, we have to reload a whole bunch of functions, as shown in the code in the code that follows.



Fortunately, Qbytearray, qstring are different from other qtl, they are always char type data internally, so overloading the relevant part operator is good. And Qvector, Qlist, and so on, the internal data type of the user-determined container is not convenient to do so, but also explains why the above Qvector not use proxy class reasons.






All right, it's almost there.









Another talk on Qvector and Qbytearray--qt's write-time copy (copy on write) technology


Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.