13 Stimmen

Behandlung einer Klasse mit einer langen Initialisierungsliste und mehreren Konstruktoren?

Ich habe ein (für mich) komplexes Objekt mit etwa 20 Datenelementen, von denen viele Zeiger auf andere Klassen sind. Für den Konstruktor habe ich also eine große, lange und komplexe Initialisierungsliste. Die Klasse hat außerdem ein Dutzend verschiedener Konstruktoren, die die verschiedenen Möglichkeiten widerspiegeln, wie die Klasse erstellt werden kann. Die meisten dieser initialisierten Elemente sind zwischen den verschiedenen Konstruktoren unverändert.

Meine Sorge dabei ist, dass ich jetzt einen großen Haufen kopierten (oder größtenteils kopierten) Codes habe, der, wenn ich der Klasse ein neues Mitglied hinzufügen muss, es möglicherweise nicht in jede der Konstruktorinitialisierungslisten schafft.

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

Dann wiederholen Sie die oben beschriebenen Schritte für die anderen Konstruktoren. Gibt es eine intelligente Art und Weise der Verwendung der Standard-Konstruktor für diese, dann ändern Sie die Ergebnisse für die anderen Konstruktoren?

15voto

veefu Punkte 2672

Wie wäre es, die gemeinsamen Felder in eine Basisklasse umzuwandeln? Der Standardkonstruktor für die Basisklasse würde die Initialisierung für die Vielzahl der Standardfelder übernehmen. Das würde etwa so aussehen:

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

Ihre Objektkonstruktoren sollten jetzt etwas übersichtlicher aussehen:

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

Ähnlich wie die Antwort von Iraimbilanja, aber ohne das Hinzufügen einer inneren Klasse für den Zugriff auf Daten, was sich auf eine Menge bestehenden Code auswirken könnte. Wenn Sie bereits eine Klassenhierarchie haben, kann es jedoch schwierig sein, diese in eine Basisklasse zu integrieren.

12voto

Knitschi Punkte 2442

Jetzt, ein paar Jahre später, haben wir C++ 11. Wenn Sie es in Ihrem Projekt verwenden können, haben Sie zwei Möglichkeiten:

Wenn die gemeinsamen Initialisierungswerte erst zur Laufzeit bekannt sind, können Sie delegierende Konstruktoren verwenden, d. h. ein Konstruktor ruft einen anderen auf.

 // function that gives us the init value at runtime.
 int getInitValue();

 class Foo
 {
     const int constant;
     int userSet;

 public:
     // initialize long member list with runtime values
     Foo() 
       : constant(getInitValue())
       , userSet(getInitValue())
     {}

     // other constructors with arguments
     Foo( int userSetArg) 
       : Foo()
       , userSet(userSetArg) 
     {
     }
 };

oder Sie können die Mitglieder direkt in der Klassendefinition initialisieren, wenn ihre Werte zur Kompilierzeit bekannt sind.

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

public:
    Foo( int userSetArg) : userSet(userSetArg){}
}

5voto

Ja, das ist möglich.
Der Einfachheit halber werde ich so tun, als ob der ursprüngliche Code so lautet:

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

Der umgestaltete Code lautet also:

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

4voto

Das hat nichts mit Konstruktoren zu tun, aber warum glauben Sie, dass Sie all diese Unterobjekte dynamisch mit new erstellen müssen? Das ist keine gute Idee - Sie sollten die dynamische Erstellung nach Möglichkeit vermeiden. Machen Sie nicht alle diese Mitglieder Zeiger - machen Sie sie tatsächliche Objekte.

4voto

dirkgently Punkte 104289

Boost::Parameter macht die Umsetzung einfach Idiom für benannte Parameter . Sehen Sie sich das an Gewinde auf SO. Dies ist vielleicht nicht genau das, was Sie brauchen, bietet aber eine gewisse Flexibilität, wenn Sie Aufrufe an den Standard-Ctor weiterleiten möchten.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X