2009-06-09 16 views
2

私はそれがすべてではないCItemオブジェクトがそう登録する必要があることが判明している間、私はコンストラクタでレジストリ(および登録の当然のを必要としませんCItemのバージョンを必要とする以下のクラスにした後オプションのリファレンスメンバー - 可能でしょうか?

class CItem 
{ 
    public: 
     CItem(CRegistry &Registry) _Registry(Registry) {Registry.Register();} 
     ~CItem() {_Registry.Unregister()}; 


    private: 
     CRegistry &_Registry; 
} 

を持っていますコード)。これをどのように実装できますか? 私がここで見ることができる唯一の解決策は、レジストリを取得してポインタとして保持することです。テンプレートを使用するなど、より洗練されたソリューションがありますか(リファレンスからポインタに切り替えるのは好きではありません)?

+3

ところで、先頭にアンダースコアが付いている名前は、標準によって予約されています。 –

+4

特にアンダースコアと大文字で始まる名前! –

+0

詳細を述べる:アンダースコアの後ろに続く大文字の名前はどこにでも予約されています。アンダースコアで始まる他の名前は、グローバル名前空間でのみ予約されているので、あなたが主張している場合はメンバ変数として使用できます(実装固有の記号を隠す可能性はほとんどありません)。 17.4.3.1.2。 –

答えて

8

。ニールが指摘しているように、完全に正当化されていない生のポインタに対する広範な不当なジハードが存在する。未処理のポインタを使用して、オブジェクトがポイントメモリの所有権を保持していないことを明示的に文書化(コメント)して、後でデストラクタにdeleteを追加するような気がしないようにします。

他のすべてのソリューションは、内部的にポインタを使用するよりも悪くなります。実装の詳細です。それが意味をなさないかどうかも検討してください。あなたのコードはポインタが有効であると想定できなくなり、クラス内のロジックが複雑になります。

最後の注意点としては
class CItem 
{ 
public: 
    CItem(CRegistry &Registry) : _Registry(&Registry) {Registry->Register();} 
    CItem() : _Registry(0) {} 
    ~CItem() { if (_Registry) _Registry->Unregister(); } 

private: 
    CRegistry *_Registry; // Pointer is not owned. Do not delete! 
}; 

に:彼らはC++の実装(コンパイラと標準ライブラリ)のための規格で予約されているプレフィックスは、単一のアンダースコアで属性がありません

+0

"属性に1つのアンダースコアを付けることはありません"。明らかでない場合は、複数のアンダースコアも使用しないでください。ダブルアンダースコアを含む名前は、すべての用途に予約されています。 –

+0

オプションのセマンティクスをより明確にするために、rawポインタではなくboost :: optionalを使用することができます。しかし、そうでなければ私は同意します。 "組み込みの"オプションのサポートのために、ポインタはうまくトリックを行います。 – jalf

5

唯一の他の選択肢は、CItem基底クラスを作成し、それからItemWithRef(参照を持つ)とItemWithoutRefを派生させることです。しかし、irはポインタを使う方がはるかに簡単で明確です。

メンバーとしての参照に関する質問の数から、参照が良好でポインタが悪いという意見がどこかに広がっているようです。これは単純にそうではありません。特にデータメンバーの場合はそうです。

そして、ちょうど下線の事を明確にする:彼らは、名前空間(クラスのすなわち外)で発生したときに/ライブラリライターをコンパイル++アンダースコアとlowervcase文字で始まるは、Cのために予約されている

  • 名をスコープアンダースコアと大文字で始まる、または二つの連続アンダースコアが含まれているthatb

  • 名は無条件コンパイラ/ライブラリ・ライターのために予約されている - あなたがあなた自身のコードでそれらを使用することはできません

1

あなたは、パラメータとしてCRegistryを取ることはありません別のコンストラクタを作成することができます。

CItem(); 

静的な「ゾンビ」の値に_Registryを初期化していますか?ポインターやサブクラスを使うよりもエレガントではないでしょうか?

+1

これを行うには、 「null」レジストリオブジェクトを作成します。レジストリオブジェクトは、おそらくRegistryクラスの変更を伴います。これは非常に悪い解決策です、IMHO。 –

+0

真。私が個人的に実装するものではありません。私は私的なポインタで行くだろう。 –

1
  1. 継承を使用してください:CItemの基底クラスを作成し、CRegisteredItemをこれから取得します。
  2. 使用ポインタ(およびオーバーロードされたコンストラクタ)ポインタへ
1

1つの明白な選択肢はprivate: static CRegistry selfRegistered;です。 ユンは、あなたがCRegistryシングルトン(または単純にスタンドアロンクラス)を作成し、その特定のインスタンスを登録するかどうかCItemコンストラクタで決めることができましたCItem::CItem() : Registry(selfRegistered) { }

0

を書くことができます。これはレジストリからアイテムを切り離し、IMHOは将来のものを変更しやすくします。

0

あなたは、おそらくこのようにテンプレートの特殊化を使用することができます:あなたは、単一のクラスを維持したい場合は、単に生のポインタに属性を変更し、それがnullにすることができ

class CRegistry 
{ 
public: 
    void Register(){} 
    void Unregister(){} 
}; 

template <class RegistryType> 
class CItem 
{ 
     public: 
       CItem(){} 
       ~CItem() {} 


}; 


template<> 
class CItem<CRegistry> 
{ 
    public: 
    CItem(CRegistry &Registry_in): Registry(Registry_in) {Registry.Register();} 
    ~CItem() {Registry.Unregister();} 


     private: 
       CRegistry& Registry; 
}; 

int main() 
{ 

    CRegistry c1; 
    CItem<CRegistry> it(c1); 
    CItem<int> it2; 
     return 0; 
} 
2

基準部材を有することにより、あなたははっきりと言っていますレジストリは、CItemごとに必要なです(有効なオブジェクトに参照をバインドする必要があり、CItemにはすべて参照メンバーがあるため)。オプションのCRegistryを実装する直接の方法は、boost::optionalを使用することです(これはNULLポインタのイディオムよりも安全で明快です)。代わりに、Null Object Patternを使用すると、no-opsとして登録、登録抹消、その他の機能を実装するCNullRegistryクラスを作成できます。次に、コンストラクタパラメータをデフォルトのCNullRegistryオブジェクトにします。

しかし、登録されていないものから登録されたCItemを明確に示す上位レベルのアプローチを検討したいと思うかもしれません。他の答えが示唆しているように、継承とテンプレートの特殊化の両方がそのメカニズムを提供します。利点は、あなたのデザインが "私は登録されています"不変量に依存できるようになることです。

0

CRegistry抽象クラスください:

 
class CRegistry 
{ 
public: 
    virtual void Register(const CItem& Item) = 0; 
    virtual void Unregister(const CItem& Item) = 0; 
}; 

は、その後2つの実装

 
class CNoopRegistry : public CRegistry 
{ 
public: 
    virtual void Register(const CItem& Item) {} 
    virtual void Unregister(const CItem& Item) {} 
}; 

class CWorkingRegistry : public CRegistry 
{ 
public: 
    virtual void Register(const CItem& Item) { /* do something useful */ } 
    virtual void Unregister(const CItem& Item) { /* do something useful */ } 
}; 

を導出し、あなたがCItemのコンストラクタに必要な方のインスタンスを渡します。

関連する問題