Подтвердить что ты не робот

Обработка класса с длинным списком инициализации и несколькими конструкторами?

У меня есть (для меня) сложный объект с примерно 20 членами данных, многие из которых являются указателями на другие классы. Поэтому для конструктора у меня большой длинный, сложный список инициализации. Класс также имеет десяток различных конструкторов, отражающих различные способы создания класса. Большинство из этих инициализированных элементов не изменяются между каждым из этих разных конструкторов.

Меня беспокоит то, что теперь у меня есть большой кусок скопированного (или в основном скопированного) кода, который, если мне нужно добавить нового члена в класс, не может попасть в каждый из списков инициализации конструктора.

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

Затем повторите, как указано выше для других конструкторов. Есть ли какой-либо умный способ использования конструктора по умолчанию для этого, а затем изменение результатов для других конструкторов?

4b9b3361

Ответ 1

Как реорганизовать общие поля в базовый класс. Конструктор по умолчанию для базового класса будет обрабатывать инициализацию для множества полей по умолчанию. Будет выглядеть примерно так:

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

Конструкторы объектов должны выглядеть немного более управляемыми, теперь:

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

Аналогичный по характеру Ириимбиландже ответ, но избегает добавления внутреннего класса для доступа к данным, что может повлиять на многие существующие коды. Однако, если у вас уже есть иерархия классов, может быть сложно включить его в базовый класс.

Ответ 2

Теперь через пару лет у нас есть С++ 11. Если вы можете использовать его в своем проекте, у вас есть два варианта:

Когда общие значения инициализации известны только во время выполнения, вы можете использовать делегирующие конструкторы, что означает, что один конструктор вызывает другой.

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

или вы можете инициализировать элементы непосредственно в определении класса, если их значения известны во время компиляции.

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

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

Ответ 3

Да, это возможно.
Для простоты я сделаю вид, что исходный код:

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

Реорганизованный код:

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

Ответ 5

Не связано с конструкторами, но почему вы думаете, что вам нужно динамически создавать все эти под-объекты с помощью нового? Это не очень хорошая идея - вы должны избегать динамического создания, где это возможно. Не делайте все эти указатели элементов - делайте их реальными объектами.

Ответ 6

Вы можете поделиться своим общим кодом в частной функции 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
   ...
 } 

Ответ 7

Прежде всего, у вас будут утечки памяти, если вы не реализуете удаление выделенных объектов в деструкторе. Поэтому вы должны определить свой деструктор и удалить там объекты.

Если вам действительно необходимо динамически распределять элементы (я не рекомендую, принадлежит ли этому классу объект-член данных), вы можете иметь частный метод, выполняющий всю инициализацию, и вы можете вызвать этот метод у своих конструкторов.

Ответ 8

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

Это не решит все ваши проблемы (в частности, не представляется возможным представить конструктор с именем и loadMetaData), но он по крайней мере скроет некоторые из конструкторов в один.

Ответ 9

Я предоставлю это, сказав, что я (очевидно) не знаю деталей вашей системы или ограничений, которые привели к вашим проектным решениям.

Как говорится, хорошим правилом является то, что когда класс начинает получать unweildy - примерно в то время, когда вы начинаете задавать вопросы о том, как с ним справиться:) - возможно, настало время реорганизовать этот класс на пару меньших подклассы.

Помните, что класс должен делать one вещь очень хорошо. Если у вас появятся большие классы, которые пытаются сделать слишком много вещей, вы дрейфовали от хорошего дизайна OO.

Ответ 10

Я бы использовал разные методы factory (статические методы), которые возвратили бы умный ptr в ваш класс. Названия методов factory также помогли бы документу WHY вам нужны все различные параметры.

Ответ 11

Вам действительно нужны 5 разных конструкторов?

Очень часто, если вам нужно преобразовать конструкторы, вам также не нужен конструктор по умолчанию.

Ваши другие конструкторы используют только одну комбинацию одной и той же вещи. Может быть, лучше всего иметь только один конструктор, который принимает все параметры, с параметром для каждого параметра, который указывает на желание вызвать какой-то параметр по умолчанию для этого параметра.

Например:

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

};

Ответ 12

Просто поместите список инициализаторов внутри MACRO и сделайте с ним.