2010-03-26 6 views
6

me encontré sobre un método que parece estar presente en todos los objetos de datos como QList, QQueue, QHash ...método indocumentado Qt setSharable

incluso investigaron hasta ahora puedo ver el código fuente de la misma, la cual es

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

en qlist.h (líneas 117).

¿Pero qué efecto tiene en el QList, QQueue, QHash ...? ¿Y está de alguna manera relacionado con el enhebrado (lo cual suena razonable)?

Gracias por cualquier respuesta, y por favor solo responda si tiene conocimiento real.

Respuesta

5

El estado compartible estado sobre el que está preguntando no tiene nada que ver con mutlithreading. En su lugar, es un detalle de implementación de las clases de datos de copia en escritura (incluso los de un solo subproceso) que distribuyen referencias al estado interno.

Considérese una clase String que se implementa utilizando vaca (para fines de ilustración, esta clase no es utilizable en contextos roscados, porque los accesos a d->refcount no están sincronizados, sino que también no asegura que el char arrary interno termina en '\0', y como bien podría comer su abuela, que han sido advertidos):

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

y una función de ejemplo de cada para mutar y métodos const:

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

}; 

Hasta ahora todo bien. Pero, ¿y si queremos proporcionar 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]; 
    } 

Esta implementación ingenua tiene un defecto. Considere el siguiente escenario:

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! 

Para evitar esto, String tiene que marcar en sí no compartible cuando se reparte una referencia a los datos internos, por lo que cualquier copia que se toma de siempre es profunda. Por lo tanto, tenemos que ajustar detach() y char & operator[] así:

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

Cuando para restablecer el estado shareable de nuevo a true de nuevo? Una técnica común es decir que las referencias al estado interno se invalidan al llamar a un método no const, por lo que es donde shareable se reinicia a true. Debido a que cada función no constante llama detach(), podemos restablecer shareable allí, por lo que finalmente se convierte en detach():

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