Инициализация классов внутри другого класса в C ++? - PullRequest
4 голосов
/ 11 ноября 2009

У меня есть это определение в заголовочном файле:

class Owner
{
private:
    // Fields
    Child* _myChild1;
public:
    // Constructors
    Owner();
    Owner(const char childName[]);
};

и эта реализация:

Owner::Owner(const char childName[])
{
//do some operations - children must be created after these ops
_myChild = new Child(childName);
}

и эта функция main ():

int main()
{
Owner("child1"); 
}

Несколько вопросов, и, пожалуйста, потерпите меня, я только начинаю с C ++ ..

  • Учитывая, что дочерние классы известны во время компиляции, правильно ли я считаю, что мне не нужно создавать их с «новым» в куче? Если так, то как? Я пытался использовать этот синтаксис в реализации Owner, но компилятор стонет («термин не оценивает функцию ..»):

_myChild(childName);

  • Однако, использование этого синтаксиса в реализации нормально, почему?

Child _myChild(childName);

  • Правильно ли я использую эту парадигму? Другими словами, как правило, если один класс переносит другой, владелец когда-либо держит только указатели на классы, которые он переносит?
  • Как бы вы, более опытные ребята, сделали это?

Спасибо за любой совет ..

Ответы [ 3 ]

5 голосов
/ 11 ноября 2009

Как это:

class Owner
{
    private:        // Fields
        Child   _myChild1;
    public:        // Constructors
        Owner();
        Owner(const char childName[]);
};

Owner::Owner()
    :_myChild1("Default Name")
{}

Owner::Owner(const char childName[])
    :_myChild1(childName)
{}

// Without more info about what you are doing it is hard to tell
// But a trivial change could be

Owner::Owner()
// Child defautl constructor called here
{
    // Do processing.
    _myChild1.updateName(name);
}

Owner::Owner(const char childName[])
// Child defautl constructor called here
{
    // Do processing.
    _myChild1.updateName(std::string("X") + childName);
}

Вопрос в том, какую обработку вам нужно сделать перед ребенком.

Как сторона не:

  • избегать подчеркивания в качестве первого символа в имени члена.
    В большинстве случаев это нормально, но есть ситуации, когда это не так. Поэтому лучше всего избегать их.
  • Вместо передачи массива символов передайте std :: string const &
4 голосов
/ 11 ноября 2009

То, что вы как бы описываете здесь, это «композиция», где один класс находится в другом классе. Здесь Owner содержит указатель на Child. Таким образом, фактическое выделение и инициализация экземпляра Child должны выполняться где-то во время выполнения.

Если вы хотите, чтобы Owner содержал экземпляр Child, который вам не нужно выделять в куче с new, просто объявите переменную-член как:

Child _myChild1;

, а не как указатель.

3 голосов
/ 11 ноября 2009

В принципе, как это работает, так:

  • Если класс Child полностью определен до класса Owner , его можно включить в Owner полностью или в виде указателя, в зависимости от того, что вы предпочитаете.
  • Если класс Child не определен полностью до класса Owner , он должен быть объявлен вперед и может быть включен только в качестве указателя. Это должно быть выделено в куче с new.

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

Вариант 1:

// will create a Child object using the default constructor.
// this is done even before doStuff() is called.

Owner::Owner(const char childName[]) {
  doStuff();
}

Вариант 2:

// will create a Child object using the Child(const char*) constructor.
// again, done before doStuff() is called.
Owner::Owner(const char childName[]): _myChild(childName) {
  doStuff()
}

Вы не можете использовать синтаксис _myChild(childName); внутри самого конструктора, поскольку _myChild уже создан до того, как он достигнет такого уровня. Child _myChild(childName); допустимо, но создает новый локальный объект с именем _myChild вместо изменения члена класса _myChild . Вероятно, это не ожидаемый результат.

Если вы хотите присвоить новое значение _myChild после его создания, выполните одно из следующих действий:

  • Предпочтительный метод : каким-либо образом изменить существующий объект (например, перегрузить оператор присваивания, чтобы вы могли выполнить _myChild = childName;, или использовать некоторую форму функции _myChild.setName(childName);).
  • Альтернативный метод : Используйте _myChild = Child(childName);, чтобы создать новый Дочерний и назначить его переменной-члену.

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

...