2012-09-25 22 views
5

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

今度は、私は(ある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; } 
}; 

は今、この段階ではそれだけで正常に動作し、デバッガで私が右を見ることができますポインタ型。しかし、今では値を設定して取得するための関数を追加したいのですが、その方法を理解できません。例としてセッターを取りに行きましょう。値を設定するには、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 ...、エラーを取得しています。

私はそれをどうやって行うのですか、私は基本的に正しい方法でやっていますか?

答えて

2

あなたがここで何をしようとしているかを達成する方法はありません。あなたが発見したように、QSharedDataPointerはそれに含まれている実際のタイプをテンプレートにする必要があります。

基本クラスをテンプレートにすることができます。

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

しかし、私はあなたがそれからどのような利益を得るのか分かりません。

+1

ありがとうございました。明らかに実際には方法はありません。代わりに 'QSharedPointer'を使用して終了しました。 – SingerOfTheFall

5

QSharedDataPointerの代わりにQExplicitlySharedDataPointerに切り替えることができます。このようにして、cAbstractDataオブジェクトへの非constポインタを取得しようとするときはいつでも、detach()は呼び出されません。QExplicitlySharedDataPointer<cAbstractData>オブジェクトをQExplicitlySharedDataPointer<cAtomicData>オブジェクトにキャストします。ただし、copy-on-writeを使用する場合は、cAbstractDataを変更するたびに手動でdetach()に電話する必要があります。たぶん、あなたのための分離を実行するラッパークラスを書くことができます。通常のポインタ(従ってバイナリcompabilityを保持)とQExplicitlySharedDataPointerが同じサイズであるためQSharedPointerが2倍のサイズである

この方法は、(this blog entryを参照)、QSharedPointerを用いて上に好まれてもよいです。

編集:あなたが(またはサブクラスの)実際に参照されるオブジェクトのタイプcAtomicDataの対象であることを保証する必要がありますので、QExplicitlySharedDataPointer<cAtomicData>からQExplicitlySharedDataPointer<cAbstractData>からキャストは、静的であることに注意してください、または行動ポインタの使用は未定義です。

2

私のアプリケーションで同様の問題がありましたが、ここで解決しました。私はBaseClassを持っています。これはPimplイディオムを使って実装されており、QExplicitlySharedDataPointerBaseClassPrivateです。このクラスは、DerivedClassのプライベートメンバーがDerivedClassPrivateを継承し、BaseClassPrivateを継承しています。

BaseClassPrivateには、baseParamという名前のフロートメンバーが1つあり、DerivedClassPrivateにはderivedParamという別のfloatパラメータがあります。

私は、次の操作を行って、この問題を解決:

  1. これは、仮想clone()を定義DerivedClassPrivate

  2. へのポインタを使用して新しい派生クラスをインスタンス化するために使用されるBaseClass(BaseClassPrivate* p)

    protectedコンストラクタを定義します。メソッドBaseClassPrivateDerivedClassPrivate

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

  3. 静的は、私はそれが便利プライベートdCasted()関数を定義するために見つけるDerivedClass

    でDポインタをキャスト。

は、それに応じていずれかbaseParam又はderivedParamを返しfoo()BaseClassPrivateDerivedClassPrivateに導入され、この仮想関数を、テストします。

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:ここ

コードです。DerivedClassから正しくBaseClassにコピーを作成します

DerivedClass derived; 
derived.setDerivedParam(1.0); 
QCOMPARE(derived.foo(), 1.0); // proving that DerivedClassPrivate::foo() is called 

BaseClass baseCopy = derived; 
QCOMPARE(baseCopy.foo(), 1.0); // proving that DerivedClassPrivate::foo() is called 
           // even after copying to a BaseClass 

からコピーを作成します

コールの仮想関数:時間

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

今、私たちは、次のようなことを行うことができますBaseClassBaseClass res元のクラスをpectingともコピーオンライト正しくします

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以来

+0

プライベートデータの参照カウントが1であっても、BaseClassでsetBaseParam()を呼び出すとディープコピーが作成されませんか? clone()を呼び出すために参照が1を超えているかどうか最初に確認する方が良いでしょうか? – UndeadKernel

+0

はい、あなたは絶対に正しいです。私はあなたのコメントを見たとき、これについて考えていました。 QSharedDataで参照カウントを知る方法がないため、残念ながら注意が必要です。 Danによって提案された基本クラスをテンプレート化するのが最善の方法だと思います。 –

+0

http://stackoverflow.com/questions/2693319/qexplicitlysharedpointer-and-inheritance –

0

を助け、あなたのタイプに::clone() functionを実装できます。

この機能があります独自のタイプの「仮想コピーコンストラクタ」をサポートできるように提供されています。そう、あなたは以下の例のように、あなた自身のタイプのために、この機能のテンプレート特殊化を宣言する必要がありますするために:上記の例で

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

、クローン()関数のテンプレートの特殊化はEmployeeDataを呼び出します。 :clone()仮想関数です。 EmployeeDataから派生したクラスは、その関数をオーバーライドして適切な多型を返すことができます。

この関数はQt 4.5で導入されました。

私はそうしています。

はどちらかあなたの抽象基底クラスとすべての派生クラスを使用すると、QSharedDataPointer::clone()から呼びたいvirtual BaseClass* clone()機能を実装する必要があるか、dと同じ内容の新しいインスタンスを作成するために、いくつかの他の方法(例えば、工場)が必要です。