QSharedData and inheritance
QSharedData and inheritance
我试图在使用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; }
};
现在,在这个阶段,它工作得很好,在调试器中,我可以看到正确的指针类型。但现在我想添加一个函数来设置和获取值,但我不明白如何做到。让我们以setter为例。要设置该值,我们必须通过cAtomicValue
类的data_
成员访问cAtomicData
类的value_
成员。但是,由于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 ...
错误。
我该如何做到这一点,从根本上说,我做这件事的方式是否正确?
您可以切换到QExplicitlySharedDataPointer
而不是QSharedDataPointer
。这样,每当您试图获得指向cAbstractData
对象的非常量指针时,就不会调用detach()
,这包括将QExplicitlySharedDataPointer<cAbstractData>
对象强制转换为QExplicitlySharedDataPointer<cAtomicData>
对象。但是,如果要使用写时复制,每次要修改cAbstractData
时,都需要手动调用detach()
。也许您可以编写一个包装类来为您执行分离。
这种方法可能比使用QSharedPointer
更可取,因为QExplicitlySharedDataPointer
的大小与普通指针相同(因此保持二进制可压缩性),而QSharedPointer
的大小是普通指针的两倍(请参阅本博客条目)。
编辑:请注意,从QExplicitlySharedDataPointer<cAbstractData>
到QExplicitlySharedDataPointer<cAtomicData>
的强制转换是静态的,因此您必须确保实际引用的对象是cAtomicData
类型(或子类)的对象,或者使用指针时的行为可能未定义。
我在应用程序中遇到了类似的问题,下面是我如何解决的。我有一个BaseClass
,它是使用Pimpl习惯用法实现的,QExplicitlySharedDataPointer
指向BaseClassPrivate
。该类由DerivedClass
继承,CCD_30的私有成员是继承BaseClassPrivate
的DerivedClassPrivate
。
CCD_ 33具有一个名为CCD_ 34的浮点成员,而CCD_。
我通过以下操作解决了这个问题:
-
定义受保护的构造函数
BaseClass(BaseClassPrivate* p)
这用于使用指向
DerivedClassPrivate
的指针实例化新的派生类 -
在
BaseClassPrivate
和DerivedClassPrivate
中定义虚拟clone()
方法只要需要深度复制,就会调用此方法来正确复制私有类。因此,我们不调用"QEexplicitySharedDataPointer::detachment()",而是检查QSharedData引用计数器是否大于1,然后调用clone。请注意,QSharedData::ref不在文档中,因此它可能随时更改(尽管它似乎不太可能很快发生)。
-
静态投射
DerivedClass
中的d指针我发现定义一个私有的
dCasted()
函数很方便。
为了测试这一点,在BaseClassPrivate
和DerivedClassPrivate
中引入了虚拟函数foo()
,它相应地返回baseParam
或derivedParam
。
这是代码:
基本类.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;
};
衍生类.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
希望这能帮助
我看不出有任何方法可以实现您在这里尝试的目标。正如您所发现的,QSharedDataPointer
需要在它所包含的实际类型上进行模板化。
您可以将基类作为模板,例如
template<class T>
class cAbstractValue
{
public:
cAbstractValue(){ }
virtual int type() = 0;
protected:
QSharedDataPointer<T> data_;
};
但我不确定你会从中得到什么好处。
由于Qt 4.5,您可以为您的类型实现::clone()
函数:
提供此功能是为了让您可以支持";虚拟复制构造函数";适合您自己的类型。为了做到这一点,您应该为自己的类型声明该函数的模板专用化,如下面的示例:
template<> EmployeeData *QSharedDataPointer<EmployeeData>::clone() { return d->clone(); }
在上面的示例中,clone()函数的模板专用化调用EmployeeData::clone(()虚拟函数。从EmployeeData派生的类可以重写该函数并返回正确的多态类型。
此功能在Qt 4.5中引入。
我已经这么做了,它很有效。
您的抽象基类和所有派生类都需要实现从QSharedDataPointer::clone()
调用的virtual BaseClass* clone()
函数,或者您需要一些其他方法(例如工厂)来创建与d
内容相同的新实例。
- C++核心准则 C35 对于接口类"A base class destructor should be either public and virtual, or protected and nonv
- 为什么C++逐位AND运算符在不同大小的操作数中表现为这样
- 为什么 Clang 不允许"and"作为函数名称?
- 位阵列上的快速AND运算
- 是否可以在 C++03 中定义'move-and-swap idiom'等效项
- BoostPython and CMake
- OpenSSL BIO and SSL_read
- Gurobi GRBModel and GRBmodel in C++
- std::visit and std::variant usage
- SHBrowseForFolder with BIF_BROWSEFORCOMPUTER and SHGetPathFr
- Directx12 and keystrokes
- different between int **arr =new int [ n]; and int a[i][j]?
- C++ getenv and setenv
- Inference pytorch C++ with alexnet and cv::imread image
- QSharedData and inheritance
- LuaBridge and Inheritance
- C++ and Sfml Inheritance
- sizeof(*this) and struct inheritance
- boost::bind, boost::shared_ptr and inheritance
- Inheritance and CRTP