2012-09-25 3 views
5

Я пытаюсь создать систему типов при использовании QSharedData. Идея проста, будет существовать ряд разных типов данных, каждый из которых будет получен из базового абстрактного класса. Я хочу использовать QSharedData для хранения фактических данных в каждом из них, но каждый из производных классов будет иметь разные данные, хранящиеся внутри. Сейчас я пытаюсь сделать самый простой пример и иметь некоторые проблемы.QSharedData и наследование

Допустим, это мои базовые чистые виртуальные классы:

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

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

Теперь предположим, что я хочу сделать класс для представления одного значения (как minmalistic например, что есть). Я выведение cAtomicValue от базового класса стоимости, и я также получение класса данных для хранения значения:

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 ....

Как мне это сделать, и я вообще делаю это правильно?

ответ

2

Я не вижу никакого способа добиться того, что вы пытаетесь здесь. Как вы уже выяснили, QSharedDataPointer необходимо настроить на фактический тип, который он содержит.

Вы можете сделать свой базовый класс шаблоном, например.

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

Но я не уверен, что я вижу, какую выгоду вы получите от этого.

+1

Спасибо. По-видимому, нет никакого способа. Вместо этого я использовал 'QSharedPointer'. – SingerOfTheFall

5

Вы можете переключиться на QExplicitlySharedDataPointer вместо QSharedDataPointer. Таким образом, detach() не будет вызываться всякий раз, когда вы пытаетесь получить указатель non-const для объекта cAbstractData, который включает в себя литье объекта QExplicitlySharedDataPointer<cAbstractData> объекта QExplicitlySharedDataPointer<cAtomicData>. Тем не менее, вам нужно будет звонить detach() вручную каждый раз, когда вы хотите внести изменения в cAbstractData, если вы собираетесь использовать copy-on-write. Возможно, вы можете написать класс-оболочку, чтобы выполнить отключение для вас.

Этот метод может быть предпочтительным по сравнению с использованием QSharedPointer, так как QExplicitlySharedDataPointer имеет тот же размер, как обычный указатель (и, следовательно, сохраняет бинарную Compability) в то время как QSharedPointer в два раза превышает размер (см this blog entry).

Edit: Обратите внимание, что отлитый из QExplicitlySharedDataPointer<cAbstractData> к QExplicitlySharedDataPointer<cAtomicData> является статическим, так что вам придется гарантировать, что объект, на который ссылается на самом деле является объектом типа cAtomicData (или подкласса), или поведение, когда использование указателя может быть неопределенным.

2

У меня была аналогичная проблема в моем приложении, и вот как я ее решил. У меня есть BaseClass, который реализован с использованием идиомы Pimpl и QExplicitlySharedDataPointer, указывая на BaseClassPrivate. Этот класс наследуется DerivedClass, чей частный член является DerivedClassPrivate, наследующим BaseClassPrivate.

BaseClassPrivate имеет один поплавочный элемент с именем baseParam и DerivedClassPrivate имеет другой параметр с поплавком derivedParam.

Я решил эту проблему, выполнив следующие действия:

  1. Определить защищенный конструктор BaseClass(BaseClassPrivate* p)

    Это используется для создания экземпляра новых производных классов с указателем на DerivedClassPrivate

  2. Определить виртуальный clone() метод как в BaseClassPrivate, так и DerivedClassPrivate

    Этот метод призван правильно копировать частный класс всякий раз, когда требуется глубокая копия. Поэтому вместо вызова «QExplicitlySharedDataPointer :: detach()» мы проверяем, больше ли счетчик ссылок QSharedData, а затем мы вызываем клон. Обратите внимание, что QSharedData :: ref не содержится в документации, поэтому это может измениться в любое время (хотя это вряд ли скоро произойдет).

  3. Статический отливать указатель 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'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.ч

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 res pecting исходного класса, а также сделать копию при записи правильно:

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 

Надеется, что это помогает

+0

Не вызвал бы setBaseParam() в BaseClass создать глубокую копию, даже если счетчик ссылок на личные данные равен 1? Не было бы лучше сначала увидеть, если ссылка выше 1 для вызова clone()? – UndeadKernel

+0

Да, ты абсолютно прав. Я думал об этом, когда увидел ваш комментарий. К сожалению, это предостережение, так как нет способа узнать счетчик ссылок с помощью QSharedData. Я думаю, что шаблонный базовый класс, предложенный Дэном, - лучший способ пойти. –

+0

Я предлагаю взглянуть на http://stackoverflow.com/questions/2693319/qexplicitlysharedpointer-and-inheritance –

0

Начиная с Qt 4.5 вы можете реализовать ::clone() function для вашего типа:

Этой функцией является при условии, что вы можете поддерживать «конструкторы виртуальных копий» для ваших собственных типов. Для того, чтобы таким образом, вы должны объявить шаблон специализацию этой функции для своего собственного типа, как на примере ниже:

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

В приведенном выше примере, специализация шаблона для функции клон() вызывает EmployeeData: : clone() виртуальная функция. Класс, полученный из EmployeeData, может переопределить эту функцию и вернуть правильный полиморфный тип.

Эта функция была введена в Qt 4.5.

Я сделал это, и он работает.

Либо ваш абстрактный базовый класс и все производные классы должны реализовать virtual BaseClass* clone() функцию, которую вы хотите позвонить из QSharedDataPointer::clone() или вам нужен некоторый другой метод (например завод), чтобы создать новый экземпляр с тем же содержанием, как d.