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; }
};

この段階では問題なく動作し、デバッガーで正しいポインターの種類を確認できます。しかし、今は値を設定して取得するための関数を追加したいのですが、その方法がわかりません。セッターを例にとってみましょう。値を設定するには、クラスのメンバーを介してクラスのvalue_メンバーにアクセスする必要があります。ただし、は基本クラスのポインタ( )を保持しているため、何らかの方法で正しい型()にキャストする必要があります。私はこれをやってみました:cAtomicDatadata_cAtomicValuedata_cAbstractDatacAtomicData

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()への非定数ポインターを取得しようとしているときに呼び出されることはありません。ただし、コピーオンライトを使用する場合は、変更を加えるたびに手動で呼び出す必要があります。ラッパークラスを記述して、デタッチを実行できるかもしれません。cAbstractDataQExplicitlySharedDataPointer<cAbstractData>QExplicitlySharedDataPointer<cAtomicData>detach()cAbstractData

QSharedPointeraQExplicitlySharedDataPointerは通常のポインタと同じサイズであり(したがってバイナリの互換性を維持します)、aQSharedPointerは2倍のサイズであるため(このブログエントリを参照)、この方法を使用するよりも優先される場合があります。

編集:QExplicitlySharedDataPointer<cAbstractData> toからのキャストQExplicitlySharedDataPointer<cAtomicData>は静的であるため、参照されるオブジェクトが実際にタイプcAtomicData(またはサブクラス)のオブジェクトであることを保証する必要があります。そうしないと、ポインターを使用したときの動作が未定義になる可能性があります。

于 2012-10-03T09:14:22.457 に答える
3

私は自分のアプリケーションで同様の問題を抱えていました。これが私がそれを解決した方法です。BaseClassPimplイディオムを使用して実装されているQExplicitlySharedDataPointerを持っていBaseClassPrivateます。このクラスは、DerivedClassそのプライベートメンバーがDerivedClassPrivate継承であるによって継承されBaseClassPrivateます。

BaseClassPrivate1つのfloatメンバーという名前がbaseParamあり、DerivedClassPrivate別のfloatパラメーターという名前がありderivedParamます。

私は次のようにしてこの問題を解決しました:

  1. 保護されたコンストラクターを定義するBaseClass(BaseClassPrivate* p)

    これは、へのポインタを使用して新しい派生クラスをインスタンス化するために使用されますDerivedClassPrivate

  2. との両方で仮想clone()メソッドを定義するBaseClassPrivateDerivedClassPrivate

    このメソッドは、ディープコピーが必要な場合にプライベートクラスを正しくコピーするために呼び出されます。したがって、「QExplicitlySharedDataPointer :: detach()」を呼び出す代わりに、QSharedData参照カウンターが1より大きいかどうかを確認してから、cloneを呼び出します。QSharedData :: refはドキュメントに含まれていないため、いつでも変更される可能性があることに注意してください(すぐに発生する可能性は低いようですが)。

  3. dポインタを静的にキャストしますDerivedClass

    プライベートdCasted()関数を定義すると便利だと思います。

これをテストするために、仮想関数がとにfoo()導入されます。これは、またはそれに応じて戻ります。BaseClassPrivateDerivedClassPrivatebaseParamderivedParam

コードは次のとおりです。

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

からコピーを作成し、元のクラス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から派生したクラスは、その関数をオーバーライドして、適切なポリモーフィック型を返す可能性があります。

この機能はQt4.5で導入されました。

私はそうしました、そしてそれは働きます。

抽象基本クラスとすべての派生クラスは、virtual BaseClass* clone()呼び出し元の関数を実装するQSharedDataPointer::clone()必要があるか、またはと同じコンテンツで新しいインスタンスを作成するために他のメソッド(ファクトリなど)が必要ですd

于 2018-03-09T06:51:21.163 に答える