5

我正在尝试在使用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; }
};

现在在这个阶段它工作得很好,在调试器中我可以看到正确的指针类型。但是现在我想添加一个设置和获取值的函数,我不明白该怎么做。我们以setter为例。要设置值,我们必须通过类的成员访问value_cAtomicDatadata_成员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 ...错误。

我该怎么做,我什至是从根本上以正确的方式做的吗?

4

4 回答 4

6

您可以切换到QExplicitlySharedDataPointer而不是QSharedDataPointer. detach()每当您尝试获取指向对象的非常量指针时,都不会调用这种方式cAbstractData,其中包括将QExplicitlySharedDataPointer<cAbstractData>对象转换为QExplicitlySharedDataPointer<cAtomicData>对象。但是,如果要使用写时复制,则detach()每次要修改时都需要手动调用。cAbstractData也许您可以编写一个包装类来为您执行分离。

这种方法可能比 using 更受欢迎QSharedPointer,因为 aQExplicitlySharedDataPointer与普通指针大小相同(因此保持二进制兼容性),而 aQSharedPointer是大小的两倍(请参阅此博客条目)。

编辑:请注意,从QExplicitlySharedDataPointer<cAbstractData>to的转换QExplicitlySharedDataPointer<cAtomicData>是静态的,因此您必须保证实际引用的对象是该类型cAtomicData(或子类)的对象,或者使用指针时的行为可能是未定义的。

于 2012-10-03T09:14:22.457 回答
3

我在我的应用程序中遇到了类似的问题,这就是我解决它的方法。我有一个BaseClass使用 Pimpl 成语实现并QExplicitlySharedDataPointer指向BaseClassPrivate. 此类DerivedClass由其私有成员继承的DerivedClassPrivate继承BaseClassPrivate

BaseClassPrivate有一个名为的浮点成员baseParamDerivedClassPrivate另一个名为的浮点参数derivedParam

我通过以下方式解决了这个问题:

  1. 定义受保护的构造函数BaseClass(BaseClassPrivate* p)

    这用于使用指向的指针实例化新的派生类DerivedClassPrivate

  2. 在和中定义一个虚clone()方法BaseClassPrivateDerivedClassPrivate

    每当需要深度复制时,都会调用此方法来正确复制私有类。因此,我们不调用“QExplicitlySharedDataPointer::detach()”,而是检查 QSharedData 引用计数器是否大于 1,然后调用 clone。请注意 QSharedData::ref 不在文档中,因此它可以随时更改(即使它似乎不太可能很快发生)。

  3. 静态转换 d 指针DerivedClass

    dCasted()我发现定义一个私有函数很方便。

为了测试这一点,在andfoo()中引入了虚函数,它返回或相应地返回。BaseClassPrivateDerivedClassPrivatebaseParamderivedParam

这是代码:

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

复制BaseClassBaseClass尊重原始类,并正确地进行写时复制:

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

希望这可以帮助

于 2014-09-21T10:56:28.617 回答
2

我看不到任何方法可以实现您在这里尝试的目标。正如您所发现的,QSharedDataPointer需要根据它包含的实际类型进行模板化。

您可以使您的基类成为模板,例如

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

但我不确定我看到你会从中得到什么好处。

于 2012-09-25T12:23:26.410 回答
0

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

于 2018-03-09T06:51:21.163 回答