2009-04-17 14 views
12

私は(私のために)約20のデータメンバーを持つ複雑なオブジェクトを持っています。その多くは他のクラスへのポインタです。だから、コンストラクタのために、私は長い、複雑な初期化リストを持っています。クラスには、クラスが作成されるさまざまな方法を反映して、12の異なるコンストラクタもあります。これらの初期化された項目のほとんどは、これらの異なるコンストラクタのそれぞれの間で変更されません。長い初期化リストと複数のコンストラクタを持つクラスを扱う?

ここでは、新しいメンバーをクラスに追加する必要がある場合、各コンストラクタの初期化リストにコピーしないようにするために、コピーされた(またはほとんどコピーされた)コードが大量にあります。

class Object 
{ 
    Object(); 
    Object(const string &Name); 
    Object (const string &Name, const string &path); 
    Object (const string &Name, const bool loadMetadata); 
    Object (const string &Name, const string &path, const bool loadMetadata); 
} 

Object::Object() : 
    name(), 
    parent_index (0), 
    rowData (new MemoryRow()), 
    objectFile(), 
    rows (new MemoryColumn (object_constants::RowName, OBJECTID, object_constants::ROWS_OID)), 
    cols (new MemoryColumn (object_constants::ColName, OBJECTID, object_constants::COLS_OID)), 
    objectName (new MemoryColumn(object_constants::ObjName, STRING, object_constants::short_name_len, object_constants::OBJECTNAME_OID)), 
    parent  (new MemoryColumn(object_constants::ParentName, STRING, object_constants::long_name_len, object_constants::PARENT_OID)), 
    parentIndex (new MemoryColumn(object_constants::ParentIndex, OBJECTID, object_constants::PARENTINDEX_OID)), 
    childCount (new MemoryColumn (object_constants::ChildCount, INTEGER, object_constants::CHILD_COUNT_OID)), 
    childList (new MemoryColumn (object_constants::ChildList, STRING, object_constants::long_name_len, object_constants::CHILD_OID)), 
    columnNames (new MemoryColumn (object_constants::ColumnNames, STRING, object_constats::short_name_len, object_constants::COLUMN_NAME)), 
    columnTypes (new MemoryColumn (object_constants::ColumnTypes, INTEGER, object_constants::COLUMN_TYPE)), 
    columnSizes (new MemoryColumn (object_constants::ColumnSizes, INTEGER, object_constants::COLUMN_SIZE)) 
{} 

次に、他のコンストラクタについて上記と同様に繰り返します。このためにデフォルトのコンストラクタを使用し、他のコンストラクタの結果を変更するスマートな方法はありますか?

答えて

13

共通のフィールドを基本クラスにリファクタリングするのはどうですか?基本クラスのデフォルトコンストラクタは、デフォルトフィールドの過多の初期化を処理します。

class BaseClass { 
    public: 
    BaseClass(); 
}; 

class Object : public BaseClass 
{ 
    Object(); 
    Object(const string &Name); 
    Object (const string &Name, const string &path); 
    Object (const string &Name, const bool loadMetadata); 
    Object (const string &Name, const string &path, const bool loadMetadata); 
}; 

BaseClass::BaseClass() : 
    parent_index (0), 
    rowData (new MemoryRow()), 
    objectFile(), 
    rows (new MemoryColumn (object_constants::RowName, OBJECTID, object_constants::ROWS_OID)), 
    cols (new MemoryColumn (object_constants::ColName, OBJECTID, object_constants::COLS_OID)), 
    objectName (new MemoryColumn(object_constants::ObjName, STRING, object_constants::short_name_len, object_constants::OBJECTNAME_OID)), 
    parent  (new MemoryColumn(object_constants::ParentName, STRING, object_constants::long_name_len, object_constants::PARENT_OID)), 
    parentIndex (new MemoryColumn(object_constants::ParentIndex, OBJECTID, object_constants::PARENTINDEX_OID)), 
    childCount (new MemoryColumn (object_constants::ChildCount, INTEGER, object_constants::CHILD_COUNT_OID)), 
    childList (new MemoryColumn (object_constants::ChildList, STRING, object_constants::long_name_len, object_constants::CHILD_OID)), 
    columnNames (new MemoryColumn (object_constants::ColumnNames, STRING, object_constats::short_name_len, object_constants::COLUMN_NAME)), 
    columnTypes (new MemoryColumn (object_constants::ColumnTypes, INTEGER, object_constants::COLUMN_TYPE)), 
    columnSizes (new MemoryColumn (object_constants::ColumnSizes, INTEGER, object_constants::COLUMN_SIZE)) 
{} 

あなたのオブジェクトのコンストラクタは今、もう少し扱いに​​なります。このようなものになりますIraimbilanjaの答えに自然の中で

Object::Object() : BaseClass() {} 
Object::Object (const string &Name): BaseClass(), name(Name) {} 
Object::Object (const string &Name, const string &path): BaseClass(), name(Name), path_(path){} 
Object::Object (const string &Name, const bool loadMetadata): BaseClass(), name(Name){} 
Object::Object (const string &Name, const string &path, const bool loadMetadata): BaseClass(), path_(path) {} 

類似しますが、データにアクセスするための内部クラスを追加することを避け、既存のコードの多くに影響を与える可能性があります。ただし、既にクラス階層を持っている場合は、それを基本クラスに組み込むことは難しいかもしれません。

+3

は実装の詳細なのでプライベート継承を使用することができます。 –

+0

または空の名前空間にベースクラスを入れますか? – jiggunjer

4

Boost::ParameterNamed Parameter Idiomを簡単に実装できます。このthreadをチェックしてください。これはあなたが必要とするものではないかもしれませんが、コールをデフォルトのctorに転送したいときに柔軟性を提供します。

+0

名前付きパラメータイディオムがどのように関連しているか説明できますか? –

+0

2番目のリンク。 – dirkgently

4

コンストラクタとは関係ありませんが、なぜこれらすべてのサブオブジェクトを新しいもので動的に作成する必要があると思いますか?これは良い考えではありません。できるだけ動的な作成を避けるべきです。それらのメンバーすべてにポインタをつけないでください。実際のオブジェクトにしてください。

+1

それは時間のいくつかの動作します。クラスが非常に大きくなり、主に未使用の場合はどうなりますか?かなり頻繁にクラスオブジェクトをコピーする必要がある場合はどうすればよいですか?異なるメンバーがサブオブジェクトの共有を諦めたらどうなるでしょうか? –

2

共通のコードをprivate init()メンバー関数で共有できます。

例:

class Object 
{ 
public: 
    Object(const string &Name); 
    Object(const string &Name, const string &path); 
    ... 
private: 
    void init(); 
}; 

Object::Object(const string &Name) 
{ 
    init(); 
    ... 
} 

Object::Object(const string &Name, const string &path) 
{ 
    init(); 
    ... 
} 

void Object::init() 
{ 
//intialization stuff 
    ... 
} 
+1

メンバーがconstの場合、これはもちろん動作しません。 – Eclipse

+3

これは、初期化していないことを意味します。これは受け入れられるかもしれませんが、通常は遅くなり、デフォルトのctorのないメンバーがいる場合は不可能になります。 –

+0

合意した、その遅い。しかし、コードの複製を避ける。 –

5

はい、それが可能です。 「すべての

class Foo { 
public: 
    Foo() : x() { } 
    Foo(int x) : x(x) { } 

    int get_a() const { return common.a; } 
    int get_b() const { return common.b; } 
    int get_x() const { return x; } 
private: 
    struct Common { 
     Common() : a(0), b(1) { } 
     int a, b; 
    } common; 
    int x; 
}; 
+0

bar.aを参照することができなくなったことを除いて、今度はbar a 。 –

+0

もちろん、それはFoo :: getA()のようなアクセサにカプセル化されるので問題にはなりません:) –

+0

私はそれをもう少し明確にするように編集しました。 –

1

まず、あなたはドン場合は、メモリリークがあるでしょう:

class Foo { 
public: 
    Foo() : a(0), b(1), x() { } 
    Foo(int x) : a(0), b(1), x(x) { } 

    int get_a() const { return a; } 
    int get_b() const { return b; } 
    int get_x() const { return x; } 
private: 
    int a, b, x; 
}; 

リファクタリング、コード、そして、次のとおりです。簡単にするために
は、私は元のコードであることをふりデストラクタで割り当てられたオブジェクトの削除を実装します。したがって、デストラクタを定義してそこのオブジェクトを削除する必要があります。

本当にメンバを動的に割り当てる必要がある場合(このクラスがデータメンバオブジェクトを所有している場合はお勧めしません)、すべての初期化を行うプライベートメソッドを持つことができ、コンストラクタからそのメソッドを呼び出すことができます。

+0

新しいMemoryColum()のすべての値がスマートポインタに入ります。これにより、Objectが破棄されたときに自動的に削除されます。理論的には。 –

0

本当に5種類のコンストラクタが必要ですか?

変換コンストラクタが必要な場合は、デフォルトのコンストラクタも必要としないことがよくあります。

あなたの他のコンストラクタはすべて同じものの異なる組み合わせをとっています。すべてのパラメータを使用するコンストラクタを1つだけ作成し、各パラメータのオプションを指定して、そのパラメータに何らかのデフォルトを呼び出す欲求を示す方がよい場合があります。例えば

class Object 
{ 
    Object (const string *Name, // can be NULL 
    const string *path, // can be NULL 
    const bool loadMetadata); 

}; 
+0

NULLポインタを文字列に変換するのは非常に意外なことですが、最後の手段として実行可能です。 –

1
Object (const string &Name = "", const string &path = "", const bool loadMetadata = false); 

これは、(特に、名前やloadMetaData持つコンストラクタを表現する方法はありません)あなたの問題のすべてを解決することはできませんが、それは、少なくとも一部が崩壊しますコンストラクタの1つになります。

1

私は(明らかに)システムの詳細や設計上の決定につながった制約についてはわかりません。

言い換えれば、クラスが不調になってくると、それを処理する方法についての質問が始まる頃です。:)そのクラスをいくつかのクラスにリファクタリングする時が来るかもしれませんより小さなサブクラス。

はクラスが事非常によくを行うことになっていることを覚えておいてください。あなたがあまりにも多くのことをしようとする大きなクラスを持つようになった場合、あなたは良いオブジェクト指向設計から逸脱しています。

0

MACROの中にイニシャライザリストを置き、それを使って終了してください。

1

スマートptrをクラスに返すファクトリメソッド(静的メソッド)を使用します。ファクトリメソッド名は、すべての異なるパラメータが必要な理由を文書化するのにも役立ちます。

+0

+1、私はそれは良い考えだと思うが、私はそれがIraimbilanjaによって言及された問題、すなわちconstメンバーと参照をどのように初期化するかを知ることはできない。これらにデフォルトを提供する場合は、さまざまなコンストラクタが必要です。 –

9

今数年後、私たちは持っているC++ 11.あなたのプロジェクトでそれを使用することができます場合は、2つの選択肢があります。

共通の初期値は、実行時にのみ知られているとき、あなたは意味し、コンストラクタを委任使用することができますあるコンストラクタが別のコンストラクタを呼び出します。

または、値がコンパイル時にわかっている場合は、クラス定義内でメンバーを直接初期化できます。

class Foo 
{ 
    const int constant = 0; 
    int userSet = 0; 

public: 
    Foo(int userSetArg) : userSet(userSetArg){} 
} 
関連する問題