2012-09-25 13 views
5

Estoy tratando de hacer un sistema de tipo mientras uso QSharedData. La idea es simple, habrá una cantidad de diferentes tipos de datos, cada uno de los cuales se derivará de la clase abstracta base. Quiero usar QSharedData para almacenar los datos reales en cada uno de ellos, pero cada una de las clases derivadas tendrá datos diferentes almacenados dentro. Intento dar el ejemplo más básico ahora y tener algunos problemas.QSharedData y herencia

Digamos que estos son mis bases clases virtuales puras:

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

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

Ahora digamos que yo quiero hacer una clase para representar un solo valor (como un ejemplo de que es minmalistic). Estoy derivar la cAtomicValue de la clase valor base, y también estoy derivar una clase de datos para mantener el valor:

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

Ahora en esta etapa funciona muy bien, y en el depurador puedo ver la derecha tipo de puntero Pero ahora quiero agregar una función para establecer y obtener el valor, y no entiendo cómo hacerlo. Tomemos al colocador como un ejemplo. Para establecer el valor, debemos acceder al miembro value_ de la clase cAtomicData a través del miembro data_ de la clase cAtomicValue. Sin embargo, dado que el data_ contiene un puntero de clase base (cAbstractData), tendré que convertirlo al tipo correcto (cAtomicData) de alguna manera. He intentado hacer esto:

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

es obvio que no funciona, ya que llama detach() y trata de hacer una copia de la clase base que no puede ya que la clase base es pura virtual. Luego probé a emitir el propio puntero:

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

pero estoy recibiendo un error invalid static_cast ....

¿Cómo lo hago, y lo estoy haciendo incluso de la forma correcta?

Respuesta

2

No veo ninguna manera de lograr lo que está intentando aquí. Como ha descubierto, el QSharedDataPointer necesita ser modelado en el tipo real que contiene.

Puede hacer que su clase base sea una plantilla, p.

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

Pero no estoy seguro de ver qué beneficio obtendrías de eso.

+1

Gracias. Aparentemente no hay manera de verdad. Terminé usando 'QSharedPointer' en su lugar. – SingerOfTheFall

5

Puede cambiar a QExplicitlySharedDataPointer en lugar de QSharedDataPointer. De esta forma, no se invocará detach() siempre que intente obtener un puntero no const al objeto cAbstractData, que incluye el lanzamiento del objeto QExplicitlySharedDataPointer<cAbstractData> a un objeto QExplicitlySharedDataPointer<cAtomicData>. Sin embargo, deberá llamar al detach() de forma manual cada vez que desee hacer una modificación al cAbstractData si va a utilizar copiar-escribir. Tal vez puedas escribir una clase contenedora para realizar el desmontaje por ti.

Este método puede ser preferido sobre el uso de QSharedPointer, ya que un QExplicitlySharedDataPointer es del mismo tamaño que un puntero normal (y por lo tanto mantiene compability binario) mientras que un QSharedPointer es dos veces el tamaño (ver this blog entry).

Editar: Tenga en cuenta que el CAST de QExplicitlySharedDataPointer<cAbstractData> a QExplicitlySharedDataPointer<cAtomicData> es estática, por lo que tendrá que garantizar que el objeto que se hace referencia en realidad es un objeto del tipo cAtomicData (o de una subclase), o el comportamiento cuando usar el puntero podría no estar definido.

2

Tuve un problema similar en mi aplicación y así es como lo resolví. Tengo un BaseClass que se implementa utilizando la expresión Pimpl y QExplicitlySharedDataPointer apuntando a BaseClassPrivate. Esta clase se hereda por DerivedClass cuyo miembro privado es DerivedClassPrivate heredando BaseClassPrivate.

BaseClassPrivate tiene un miembro del flotador llamado baseParamDerivedClassPrivate y tiene otro parámetro llamado flotador derivedParam.

He resuelto este problema haciendo lo siguiente:

  1. Definir un constructor protegida BaseClass(BaseClassPrivate* p)

    Esto se utiliza para crear instancias nuevas clases derivadas con un puntero a DerivedClassPrivate

  2. Definir un Portal clone() método en ambos BaseClassPrivate y DerivedClassPrivate

    Se llama a este método para copiar correctamente la clase privada siempre que se necesite una copia profunda. Entonces, en lugar de llamar 'QExplicitlySharedDataPointer :: detach()', verificamos si el contador de referencia QSharedData es mayor que 1, y luego llamamos clone. Tenga en cuenta que QSharedData :: ref no figura en la documentación, por lo que puede cambiar en cualquier momento (aunque parece improbable que suceda pronto).

  3. estática del puntero de d en DerivedClass

    Me resulta conveniente definir una función privada dCasted().

Para probar esta la función virtual foo() se introduce en BaseClassPrivate y DerivedClassPrivate, que devuelve ya sea baseParam o derivedParam en consecuencia.

Aquí está el código:

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

Ahora, podemos hacer cosas como:

llamar a funciones virtuales:

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

Haga copias de DerivedClass a BaseClass correctamente:

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

realizar copias de los BaseClass a BaseClass res pecting la clase original y también hacer una copia en escritura correcta:

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 

Esperanza esto ayuda

+0

¿No llamaría a setBaseParam() en la BaseClass para crear una copia profunda incluso si el recuento de referencia de los datos privados es 1? ¿No sería mejor ver primero si la referencia está por encima de 1 para llamar a clone()? – UndeadKernel

+0

Sí, tienes toda la razón. Estaba pensando en esto cuando vi tu comentario. Lamentablemente, esta es una advertencia ya que no hay forma de conocer el recuento de referencias con QSharedData. Creo que la mejor manera de hacerlo es crear plantillas de la clase base como lo sugirió Dan. –

+0

Sugiero echar un vistazo a http://stackoverflow.com/questions/2693319/qexplicitlysharedpointer-and-heitance –

0

Desde Qt 4.5 se puede implementar el ::clone() function para su tipo:

Esta función es proporcionado para que pueda admitir "constructores de copia virtuales" para sus propios tipos. Con el fin de así, debe declarar una plantilla de especialización de esta función para su propio tipo, como el siguiente ejemplo:

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

En el ejemplo anterior, la especialización de plantilla para la función clone() llama al employeeData: : función virtual clone() Una clase derivada de EmployeeData podría anular esa función y devolver el tipo polimórfico adecuado.

Esta función se introdujo en Qt 4.5.

Lo he hecho y funciona.

Puede ser que su clase base abstracta y todas las clases derivadas necesitan para implementar una función virtual BaseClass* clone() que se dice de QSharedDataPointer::clone() o necesita algún otro método (por ejemplo, la fábrica) para crear una nueva instancia con el mismo contenido que d.