QSharedData and inheritance

I am trying to create a type system when using QSharedData. The idea is simple, there will be a number of different data types, each of which will be obtained from a base abstract class. I want to use QSharedDatato store the actual data in each of them, but each of the derived classes will have different data stored inside. Now I am trying to make the simplest example and have some problems.

Let's say these are my base pure virtual classes:

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

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

Now let's say that I want to create a class to represent a single value (as a minimal example). I get cAtomicValuefrom the base class of values, and I also get a data class to store the value:

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

, . , , . . , value_ cAtomicData data_ cAtomicValue. , data_ (cAbstractData), - (cAtomicData). :

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

, detach() , , . :

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

invalid static_cast ....

, ?

+5
4

, . , QSharedDataPointer , .

, .

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

, , .

+2

QExplicitlySharedDataPointer QSharedDataPointer. detach() , cAbstractData, QExplicitlySharedDataPointer<cAbstractData> QExplicitlySharedDataPointer<cAtomicData>. detach() , cAbstractData, copy-on-write. , -, .

QSharedPointer, QExplicitlySharedDataPointer , (, , ), a QSharedPointer (. ).

: , QExplicitlySharedDataPointer<cAbstractData> QExplicitlySharedDataPointer<cAtomicData> , , , , cAtomicData ( ), undefined.

+5

, . BaseClass, Pimpl QExplicitlySharedDataPointer, BaseClassPrivate. DerivedClass, DerivedClassPrivate BaseClassPrivate.

BaseClassPrivate float baseParam DerivedClassPrivate float derivedParam.

:

  • BaseClass(BaseClassPrivate* p)

    DerivedClassPrivate

  • clone() BaseClassPrivate, DerivedClassPrivate

    , . "QExplicitlySharedDataPointer:: detach()" , QSharedData, . , QSharedData:: ref , ( ).

  • d- DerivedClass

    dCasted().

, foo() BaseClassPrivate DerivedClassPrivate, baseParam derivedParam.

:

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

, :

:

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

DerivedClass BaseClass :

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

BaseClass BaseClass , :

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

,

+3

Qt 4.5 ::clone() :

" " . , , :

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

clone() EmployeeData:: clone(). , EmployeeData, .

This feature was introduced in Qt 4.5.

I did it and it works.

Either your abstract base class and all derived classes must implement the function virtual BaseClass* clone()that you call from QSharedDataPointer::clone(), or you need some other method (such as a factory) to create a new instance with the same contents as d.

0
source

All Articles