2010-03-26 11 views
6

je suis tombé sur une méthode qui semble être présent dans tous les objets de données comme QList, QQueue, QHash ...Qt méthode non documentée setSharable

Je a même étudié jusqu'à présent, je peux voir le code source de celui-ci, qui est

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

dans qlist.h (lignes 117).

Mais quel effet cela at-il sur le QList, QQueue, QHash ...? Et est-ce lié d'une manière ou d'une autre au threading (ce qui semble raisonnable)?

Merci pour toute réponse, et s'il vous plaît ne répondez que si vous avez une connaissance réelle.

Répondre

5

L'état partageable que vous demandez n'a rien à voir avec mutlithreading. Il s'agit plutôt d'un détail d'implémentation de classes de données de copie à l'écriture (même celles à un seul thread) qui distribuent des références à l'état interne.

Tenir compte une classe String qui est mis en œuvre à l'aide CoW (à des fins d'illustration, cette classe ne sont pas utilisables dans des contextes filetés, car les accès à d->refcount ne sont pas synchronisés, il ne garantit pas que la char interne se termine par arrary '\0', et pourrait aussi bien manger votre grand-mère, vous avez été mis en garde):

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

et une fonction d'échantillonnage chacun pour les méthodes et const muter:

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]; 
    } 

}; 

Jusqu'ici tout va bien. Mais que faire si nous voulons fournir un 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]; 
    } 

Cette implémentation naïve a un défaut. Considérez le scénario suivant:

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! 

Pour éviter cela, String doit se marquer non partageable quand il remet une référence aux données internes, de sorte que toute copie qui est tirée de c'est toujours profond. Donc, nous devons ajuster detach() et char & operator[] comme ceci:

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]; 
    } 

Quand réinitialiser l'état shareable retour à true à nouveau? Une technique courante consiste à dire que les références à l'état interne sont invalidées lors de l'appel d'une méthode non-const, c'est ainsi que shareable est réinitialisé à true. Étant donné que toutes les fonctions non-const appelle detach(), nous pouvons réinitialiser il shareable, de sorte que detach() devient enfin:

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