2012-09-25 16 views
5

Sto provando a creare un sistema di tipi durante l'utilizzo di QSharedData. L'idea è semplice, ci saranno diversi tipi di dati, ognuno dei quali sarà derivato dalla classe astratta di base. Voglio utilizzare QSharedData per memorizzare i dati effettivi in ​​ciascuno di essi, ma ciascuna delle classi derivate avrà dati diversi memorizzati all'interno. Sto cercando di fare l'esempio più semplice ora, e avendo qualche problema.QSharedData ed ereditarietà

Diciamo che queste sono le mie basi puri classi virtuali:

class cAbstractData: public QSharedData 
{ 
public: 
    cAbstractData(){ } 
    virtual int type() = 0; 
}; 

class cAbstractValue 
{ 
public: 
    cAbstractValue(){ } 
    virtual int type() = 0; 
protected: 
    QSharedDataPointer<cAbstractData>data_; 
}; 

Ora diciamo che voglio fare una classe per rappresentare un singolo valore (come ad esempio minmalistic che è). Sto derivare la cAtomicValue dalla classe di valore di base, e sto anche derivare una classe di dati per contenere il valore:

class cAtomicData:public cAbstractData 
{ 
public: 
    cAtomicData() { value_ = 0; } 
    int type(){ return 1; } 
    QVariant value_;//the actual value 
}; 

class cAtomicValue:public cAbstractValue 
{ 
public: 
    cAtomicValue() { 
     data_ = new cAtomicData;//creating the data object. 
    } 
    int type(){ return 1; } 
}; 

Ora, in questa fase funziona bene, e nel debugger posso vedere il diritto tipo di puntatore. Ma ora voglio aggiungere una funzione per impostare e ottenere il valore, e non riesco a capire come farlo. Prendiamo il setter come esempio. Per impostare il valore, è necessario accedere al membro value_ della classe cAtomicData tramite il membro data_ della classe cAtomicValue. Tuttavia, poiché lo data_ contiene un puntatore di classe base (cAbstractData), dovrò farlo in modo corretto (cAtomicData) in qualche modo. Ho provato a fare questo:

template<class T> void set(T value) 
{ 
    static_cast<cAtomicData*>(data_.data())->value_ = value; 
} 

ovviamente non funziona, perché è chiamato detach() e cerca di fare una copia della classe di base, che non è possibile in quanto la classe base è pura virtuale. Poi ho provato a lanciare il puntatore stesso:

static_cast<cAtomicData*>(data_)->value_ = value; 

ma sto ottenendo un errore invalid static_cast ....

Come faccio, e lo sto facendo anche nel modo giusto?

risposta

2

Non vedo alcun modo per ottenere ciò che stai tentando qui. Come hai scoperto, QSharedDataPointer deve essere impostato sul tipo effettivo che contiene.

Puoi rendere la tua classe base un modello, ad es.

template<class T> 
class cAbstractValue 
{ 
public: 
    cAbstractValue(){ } 
    virtual int type() = 0; 
protected: 
    QSharedDataPointer<T> data_; 
}; 

Ma non sono sicuro di vedere quale vantaggio si otterrebbe da quello.

+1

Grazie. Apparentemente non c'è davvero alcun modo. Alla fine ho usato 'QSharedPointer'. – SingerOfTheFall

5

È possibile passare a QExplicitlySharedDataPointer anziché a QSharedDataPointer. In questo modo, detach() non verrà chiamato ogni volta che si sta tentando di ottenere un puntatore non const sull'oggetto cAbstractData, che include il cast dell'oggettosu un oggetto QExplicitlySharedDataPointer<cAtomicData>. Tuttavia, sarà necessario chiamare lo detach() manualmente ogni volta che si desidera apportare una modifica allo cAbstractData se si intende utilizzare la funzione copy-on-write. Forse puoi scrivere una classe wrapper per eseguire il distacco per te.

Questo metodo può essere preferenziale rispetto all'utilizzo QSharedPointer, poiché un QExplicitlySharedDataPointer è la stessa dimensione di un puntatore normale (e quindi mantiene compability binario) mentre una QSharedPointer è il doppio (vedi this blog entry).

Edit: Nota che il cast QExplicitlySharedDataPointer<cAbstractData>-QExplicitlySharedDataPointer<cAtomicData> è statica, in modo da avere per garantire che l'oggetto a cui fa riferimento in realtà è un oggetto di tipo cAtomicData (o di una sottoclasse), o il comportamento quando usare il puntatore potrebbe essere indefinito.

2

Ho avuto un problema simile nella mia domanda ed ecco come ho risolto. Ho un BaseClass implementato utilizzando l'idioma Pimpl e QExplicitlySharedDataPointer che punta a BaseClassPrivate. Questa classe è ereditata da DerivedClass il cui membro privato è un DerivedClassPrivate che eredita BaseClassPrivate.

BaseClassPrivate un membro float denominato baseParam e DerivedClassPrivate ha un altro parametro float denominato derivedParam.

ho risolto questo problema nel modo seguente:

  1. definire un costruttore protetto BaseClass(BaseClassPrivate* p)

    Questo viene utilizzato per creare un'istanza di nuove classi derivate con un puntatore a DerivedClassPrivate

  2. Definire un virtuale clone() metodo in entrambi BaseClassPrivate e DerivedClassPrivate

    Questo metodo viene chiamato per copiare correttamente la classe privata ogni volta che è necessaria una copia profonda. Quindi, anziché chiamare 'QExplicitlySharedDataPointer :: detach()', controlliamo se il contatore di riferimento QSharedData è maggiore di 1, e quindi chiamiamo clone. Si noti che QSharedData :: ref non è presente nella documentazione, quindi può cambiare in qualsiasi momento (anche se sembra improbabile che accada presto).

  3. statico cast del puntatore d in DerivedClass

    trovo conveniente definire una funzione privata dCasted().

Per testare questa funzione virtuale foo() viene introdotto in BaseClassPrivate e DerivedClassPrivate, che restituisce baseParam o derivedParam conseguenza.

Ecco il codice:

BaseClass.h

class BaseClass 
{ 
public: 
    BaseClass() : d(new BaseClassPrivate()) {} 
    BaseClass(const BaseClass& other) : d(other.d) {} 
    BaseClass& operator =(const BaseClass& other) {d = other.d; return *this;} 
    virtual ~BaseClass() {} 

    float baseParam() const {return d->baseParam;} 
    void setBaseParam(float value) { 
     detach(); // instead of calling d.detach() 
     d->baseParam = value; 
    } 

    float foo() const {return d->foo();} 

protected: 
    BaseClass(BaseClassPrivate* p) : d(p) {} 
    void detach() { 
     // if there's only one reference to d, no need to clone. 
     if (!d || d->ref == 1) return; // WARNING : d->ref is not in the official Qt documentation !!! 
     d = d->clone(); 
    } 
    QExplicitlySharedDataPointer<BaseClassPrivate> d; 
}; 

DerivedClass.h

class DerivedClass : public BaseClass 
{ 
public: 
    DerivedClass() : BaseClass(new DerivedClassPrivate()) {} 

    float derivedParam() const {return dCasted()->derivedParam;} 
    void setDerivedParam(float value) { 
     detach(); // instead of calling d.detach(); 
     dCasted()->derivedParam = value; 
    } 

private: 
    DerivedClassPrivate* dCasted() const {return static_cast<DerivedDataPrivate*>(d.data());} 
}; 

BaseClassPrivate.h

class BaseClassPrivate : public QSharedData 
{ 
public: 
    BaseClassPrivate() : QSharedData(), baseParam(0.0) {} 
    BaseClassPrivate(const BaseClassPrivate& other) : 
     QSharedData(other), baseParam(other.baseParam) {} 
    virtual ~BaseClassPrivate() {} 

    float baseParam; 
    virtual float foo() const {return baseParam;} 

    virtual BaseClassPrivate* clone() const { 
     return new BaseClassPrivate(*this); 
    } 
}; 

DerivedClassPrivate.h

class DerivedClassPrivate : public BaseClassPrivate 
{ 
public: 
    DerivedClassPrivate() : BaseClassPrivate(), derivedParam(0.0) {} 
    DerivedClassPrivate(const DerivedClassPrivate& other) : 
     BaseClassPrivate(other), derivedParam(other.derivedParam) {} 

    float derivedParam; 
    virtual float foo() const {return derivedParam;} 

    virtual BaseClassPrivate* clone() const { 
     return new DerivedClassPrivate(*this); 
    } 
}; 

Ora, possiamo fare cose come:

chiamata funzioni virtuali:

DerivedClass derived; 
derived.setDerivedParam(1.0); 
QCOMPARE(derived.foo(), 1.0); // proving that DerivedClassPrivate::foo() is called 

Creare copie da DerivedClass a BaseClass correttamente:

BaseClass baseCopy = derived; 
QCOMPARE(baseCopy.foo(), 1.0); // proving that DerivedClassPrivate::foo() is called 
           // even after copying to a BaseClass 

eseguire copie da BaseClass a BaseClass res pecting classe originale e anche fare un copy-on-scrivere correttamente:

BaseClass bbCopy(baseCopy);  // make a second copy to another BaseClass 
QCOMPARE(bbCopy.foo(), 1.0); // still calling DerivedClassPrivate::foo() 

// copy-on-write 
baseCopy.setBaseParam(2.0);  // this calls the virtual DerivedClassPrivate::clone() 
           // even when called from a BaseClass 
QCOMPARE(baseCopy.baseParam(), 2.0); // verify the value is entered correctly 
QCOMPARE(bbCopy.baseParam(), 1.0); // detach is performed correctly, bbCopy is 
             // unchanged 
QCOMPARE(baseCopy.foo(), 1.0); // baseCopy is still a DerivedClass even after detaching 

Spero che questo aiuti

+0

Non chiamare setBaseParam() nella BaseClass crea una copia profonda anche se il conteggio dei riferimenti dei dati privati ​​è 1? Non sarebbe meglio prima vedere se il riferimento è superiore a 1 per chiamare clone()? – UndeadKernel

+0

Sì, hai assolutamente ragione. Stavo pensando a questo quando ho visto il tuo commento. Sfortunatamente questo è un avvertimento poiché non c'è modo di conoscere il conteggio dei riferimenti con QSharedData. Immagino che il templating della classe base come suggerito da Dan sia il modo migliore per andare. –

+0

Suggerisco di dare un'occhiata a http://stackoverflow.com/questions/2693319/qexplicitlysharedpointer-and-inheritance –

0

Dal momento che Qt 4.5 è possibile implementare la ::clone() function per il vostro tipo:

Questa funzione è fornito in modo da poter supportare "costruttori di copie virtuali" per i propri tipi. Al fine di così, si dovrebbe dichiarare un modello di specializzazione di questa funzione per il proprio tipo, come nell'esempio qui sotto:

template<> 
EmployeeData *QSharedDataPointer<EmployeeData>::clone() 
{ 
    return d->clone(); 
} 

Nell'esempio di cui sopra, la specializzazione del modello per la funzione clone() chiama l'EmployeeData: : clone() funzione virtuale. Una classe derivata da EmployeeData potrebbe sovrascrivere quella funzione e restituire il tipo polimorfico corretto.

Questa funzione è stata introdotta in Qt 4.5.

Ho fatto così e funziona.

O la vostra classe di base astratta e tutte le classi derivate devono implementare una funzione virtual BaseClass* clone() che si dice da QSharedDataPointer::clone() o avete bisogno di qualche altro metodo (ad esempio di fabbrica) per creare una nuova istanza con lo stesso contenuto di d.

Problemi correlati