QSharedData和继承

时间:2012-09-25 08:34:37

标签: c++ qt reference-counting 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_;
};

现在让我们说我想创建一个表示单个值的类(作为一个简单的例子)。我从基值类派生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 ...错误。

我该怎么做,我是否从根本上以正确的方式做到了?

3 个答案:

答案 0 :(得分:6)

您可以切换为QExplicitlySharedDataPointer而不是QSharedDataPointer。这样,只要您尝试获取指向detach()对象的非const指针,就不会调用cAbstractData,其中包括将QExplicitlySharedDataPointer<cAbstractData>对象转换为QExplicitlySharedDataPointer<cAtomicData>对象。但是,如果要使用copy-on-write,每次要对detach()进行修改时,都需要手动调用cAbstractData。也许你可以编写一个包装类来为你执行分离。

此方法可能优先使用QSharedPointer,因为QExplicitlySharedDataPointer与普通指针的大小相同(因此保持二进制兼容性),而QSharedPointer是两倍大小(见this blog entry)。

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

答案 1 :(得分:3)

我的应用程序中遇到了类似的问题,这就是我如何解决它的问题。我有BaseClass使用Pimpl习惯用法QExplicitlySharedDataPointer指向BaseClassPrivate。此类由DerivedClass继承,其私有成员为DerivedClassPrivate,继承BaseClassPrivate

BaseClassPrivate有一个名为baseParam的浮动成员,DerivedClassPrivate有另一个名为derivedParam的浮点参数。

我解决了这个问题:

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

    这用于使用指向DerivedClassPrivate

  2. 的指针实例化新的派生类
  3. clone()BaseClassPrivate

    中定义虚拟DerivedClassPrivate方法

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

  4. 静态将d指针投射到DerivedClass

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

  5. 为了对此进行测试,foo()BaseClassPrivate中引入了虚拟函数DerivedClassPrivate,相应地返回baseParamderivedParam

    以下是代码:

    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
    

    希望这有帮助

答案 2 :(得分:0)

从Qt 4.5开始,您可以为您的类型实现::clone() function

  

提供此功能是为了支持&#34;虚拟副本构造函数&#34;为您自己的类型。为此,您应该为您自己的类型声明此函数的模板特化,如下例所示:

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

在上面的示例中,clone()函数的模板特化调用EmployeeData :: clone()虚函数。从EmployeeData派生的类可以覆盖该函数并返回正确的多态类型。

     

此功能在Qt 4.5中引入。

我已经这样做了,但它确实有效。

您的抽象基类和所有派生类都需要实现您从virtual BaseClass* clone()调用的QSharedDataPointer::clone()函数,或者您需要一些其他方法(例如工厂)来创建一个新实例与d相同的内容。