Я пытаюсь сделать систему типов при использовании QSharedData
, Идея проста, будет несколько разных типов данных, каждый из которых будет производным от базового абстрактного класса. Я хочу использовать QSharedData
хранить фактические данные в каждом из них, но каждый из производных классов будет иметь разные данные, хранящиеся внутри. Я пытаюсь сделать самый простой пример сейчас, и у меня есть некоторые проблемы.
Допустим, это мои базовые чисто виртуальные классы:
class cAbstractData: public QSharedData
{
public:
cAbstractData(){ }
virtual int type() = 0;
};
class cAbstractValue
{
public:
cAbstractValue(){ }
virtual int type() = 0;
protected:
QSharedDataPointer<cAbstractData>data_;
};
Теперь предположим, что я хочу создать класс для представления одного значения (в качестве минималистического примера). Я извлекаю 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 ...
ошибка.
Как мне это сделать, и я вообще правильно делаю это правильно?
Я не вижу способа добиться того, что вы пытаетесь здесь. Как вы обнаружили, QSharedDataPointer
должен быть основан на фактическом типе, который это содержит.
Вы можете сделать свой базовый класс шаблоном, например,
template<class T>
class cAbstractValue
{
public:
cAbstractValue(){ }
virtual int type() = 0;
protected:
QSharedDataPointer<T> data_;
};
Но я не уверен, что вижу, какую пользу ты бы получил от этого.
Вы можете переключиться на QExplicitlySharedDataPointer
вместо QSharedDataPointer
. Таким образом detach()
не будет вызываться всякий раз, когда вы пытаетесь получить неконстантный указатель на cAbstractData
объект, который включает в себя приведение QExplicitlySharedDataPointer<cAbstractData>
возражать против QExplicitlySharedDataPointer<cAtomicData>
объект. Однако вам нужно будет позвонить detach()
вручную каждый раз, когда вы хотите внести изменения в cAbstractData
если вы собираетесь использовать копирование при записи. Может быть, вы можете написать класс-обертку, чтобы выполнить отсоединение для вас.
Этот метод может быть предпочтительнее, чем использование QSharedPointer
, с QExplicitlySharedDataPointer
имеет тот же размер, что и обычный указатель (и, следовательно, сохраняет двоичную совместимость), в то время как QSharedPointer
в два раза больше (см. эта запись в блоге).
Редактировать: Обратите внимание, что актерский состав от QExplicitlySharedDataPointer<cAbstractData>
в QExplicitlySharedDataPointer<cAtomicData>
является статическим, поэтому вам нужно будет гарантировать, что объект, на который ссылаются, фактически является объектом типа cAtomicData
(или подкласса), или поведение при использовании указателя может быть неопределенным.
У меня была похожая проблема в моем приложении, и вот как я ее решил. у меня есть BaseClass
который реализуется с использованием идиомы Pimpl и QExplicitlySharedDataPointer
указывая на BaseClassPrivate
, Этот класс наследуется DerivedClass
чей частный член DerivedClassPrivate
наследуя BaseClassPrivate
,
BaseClassPrivate
имеет одного члена с плавающей точкой по имени baseParam
а также DerivedClassPrivate
имеет другой параметр с именем float derivedParam
,
Я решил эту проблему следующим образом:
Определить защищенный конструктор BaseClass(BaseClassPrivate* p)
Это используется для создания новых производных классов с указателем на DerivedClassPrivate
Определить виртуальный clone()
метод в обоих BaseClassPrivate
а также DerivedClassPrivate
Этот метод вызывается для правильного копирования закрытого класса всякий раз, когда требуется глубокая копия. Таким образом, вместо вызова «QExplicitlySharedDataPointer :: detach ()» мы проверяем, является ли счетчик ссылок QSharedData больше 1, и затем вызываем клон. Обратите внимание, что 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'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);
}
};
Теперь мы можем сделать такие вещи, как:
Вызов виртуальных функций:
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
Надеюсь это поможет
Начиная с Qt 4.5 вы можете реализовать ::clone()
функция для вашего типа:
Эта функция предоставляется для того, чтобы вы могли поддерживать «конструкторы виртуальных копий» для своих собственных типов. Для этого вам следует объявить специализацию шаблона для этой функции для вашего собственного типа, как в примере ниже:
template<> EmployeeData *QSharedDataPointer<EmployeeData>::clone() { return d->clone(); }
В приведенном выше примере специализация шаблона для функции clone () вызывает виртуальную функцию EmployeeData :: clone (). Класс, производный от EmployeeData, может переопределить эту функцию и вернуть правильный полиморфный тип.
Эта функция была введена в Qt 4.5.
Я так и сделал, и это работает.
Ваш абстрактный базовый класс и все производные классы должны реализовать virtual BaseClass* clone()
функция, из которой вы бы позвонили QSharedDataPointer::clone()
или вам нужен какой-то другой метод (например, фабрика) для создания нового экземпляра с тем же содержимым, что и d
,