Qt undocumented setSharable method

I came across a method that seems to be present in all data objects such as QList , QQueue , QHash ...

I even researched so far, I see the source code, which

 inline void setSharable(bool sharable) { if (!sharable) detach(); d->sharable = sharable; } 

in qlist.h (lines 117).

But what effect does it have on QList , QQueue , QHash ...? And is this in any way related to streams (which sounds reasonable)?

Thanks for any answer, and please only reply if you have actual knowledge.

+6
c ++ qt qlist
source share
2 answers

The consistent state you are asking for has nothing to do with mutlithreading. Instead, it is an implementation detail of copy-write data classes (even single-threaded) that pass references to internal state.

Consider the String class, which is implemented using CoW (to illustrate, this class cannot be used in stream contexts, since calls to d->refcount not synchronized, it also does not guarantee that the internal char arrary ends with '\0' , and can also eat your grandmother, you were warned):

 struct StringRep { StringRep() : capacity(0), size(0), refcount(0), sharable(true), data(0) {} ~StringRep() { delete[] data; } size_t capacity, size, refcount; bool sharable; // later... char * data; }; class String { StringRep * d; public: String() : d(new StringRep) { ++d->refcount; } ~String() { if (--d->refcount <= 0) delete d; } explicit String(const char * s) : d(new StringRep) { ++d->refcount; d->size = d->capacity = strlen(s); d->data = new char[d->size]; memcpy(d->data, s, d->size); } String(const String &other) : d(other.d) { ++d->refcount; } void swap(String &other) { std::swap(d, other.d); } String &operator=(const String &other) { String(other).swap(*this); // copy-swap trick return *this; } 

And an example function for mutating and const methods:

  void detach() { if (d->refcount == 1) return; StringRep * newRep = new StringRep(*d); ++newRep->refcount; newRep->data = new char[d->size]; memcpy(newRep->data, d->data, d->size); --d->refcount; d = newRep; } void resize(size_t newSize) { if (newSize == d->size) return; detach(); // mutator methods need to detach if (newSize < d->size) { d->size = newSize; } else if (newSize > d->size) { char * newData = new char[newSize]; memcpy(newData, d->data, d->size); delete[] d->data; d->data = newData; } } char operator[](size_t idx) const { // no detach() here, we're in a const method return d->data[idx]; } }; 

So far so good. But what if we want to provide a mutable operator[] ?

  char & operator[](size_t idx) { detach(); // make sure we're not changing all the copies // in case the returned reference is written to return d->data[idx]; } 

This naive implementation has a drawback. Consider the following scenario:

  String s1("Hello World!"); char & W = s1[7]; // hold reference to the W assert( W == 'W' ); const String s1(s2); // Shallow copy, but s1, s2 should now // act independently W = 'w'; // modify s1 _only_ (or so we think) assert( W == 'w' ); // ok assert( s1[7] == 'w' ); // ok assert( s2[7] == 'W' ); // boom! s2[7] == 'w' instead! 

To prevent this, String should mark itself inseparable when it passes a reference to internal data, so any copy made from it is always deep. So, we need to configure detach() and char & operator[] as follows:

  void detach() { if (d->refcount == 1 && /*new*/ d->sharable) return; // rest as above } char & operator[](size_t idx) { detach(); d->shareable = false; // new return d->data[idx]; } 

When before reset shareable state return to true again? A common method is to state that references to internal state are invalid when invoking a non-constant method, so where shareable is reset back to true . Since each non-constant function calls detach() , we can reset shareable , so detach() eventually becomes:

  void detach() { if (d->refcount == 1 && d->sharable) { d->sharable = true; // new return; } d->sharable = true; // new StringRep * newRep = new StringRep(*d); ++newRep->refcount; newRep->data = new char[d->size+1]; memcpy(newRep->data, d->data, d->size+1); --d->refcount; d = newRep; } 
+5
source share

No one could say more clearly:

http://qt.nokia.com/doc/4.6/implicit-sharing.html

It is common practice to implement containers this way.

+6
source share

All Articles