2010-05-30 3 views
0

Я пытаюсь создать экземпляр класса внутри класса, так что внешний класс содержит внутренний класс.Создание экземпляра класса внутри класса

Это мой код:

#include <iostream> 
#include <string> 

class Inner { 
    private: 
     std::string message; 

    public: 
     Inner(std::string m); 
     void print() const; 
}; 

Inner::Inner(std::string m) { 
    message = m; 
} 

void Inner::print() const { 
    std::cout << message << std::endl; 
    std::cout << message << std::endl; 
} 

class Outer { 
    private: 
     std::string message; 
     Inner in; 

    public: 
     Outer(std::string m); 
     void print() const; 
}; 

Outer::Outer(std::string m) { 
    message = m; 
} 

void Outer::print() const { 
    std::cout << message << std::endl; 
} 

int main() { 
    Outer out("Hello world."); 
    out.print(); 

    return 0; 
} 

«Внутренний в», это моя попытка, содержащая внутренний внутри внешнего, однако, когда я компилирую, я получаю сообщение об ошибке, что не существует никакой функции согласования для вызова для Inner :: Inner(). Что я сделал не так?

Спасибо.

ответ

6

Вы должны использовать списки инициализации для инициализации членов класса:

Inner::Inner(const std::string& m) 
    : message(m) 
{ 
} 

Outer::Outer(const std::string& m) 
    : in(m) 
{ 
} 

(Обратите внимание, что я передал строки в const ссылки, которая лучше, чем передача их по значению См this answer для того, как пройти. аргументы функции.)

Таким образом, вы можете точно указать, какие конструкторы следует вызывать для членов класса.

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


Edit: Обратите внимание, что, если у вас есть более одного члена класса, все они инициализируются таким образом, через запятую:

Outer::Outer(const std::string& m) : in1(m), in2(m), in3() {} 

Обратите внимание, что порядка инициализации члены класса определяются их порядком декларации в определении класса, а не порядком их появления в списке инициализации. Лучше не полагаться на порядок инициализации, так как изменение этого в определении класса затем создало бы очень тонкую ошибку в определении конструктора. Если вы не можете избежать этого, поместить комментарий, помимо декларации членов класса:

class outer { 
    public: 
    outer(const inner& in) 
    : in_(in), rin_(in_) // depends on proper declaration order of in_ and rin_ 
    {} 
    private: 
    inner in_; // declaration order matters here!1 
    inner& rin_; // (see constructor for details) 
}; 

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


Деструкторы, BTW, всегда вызываются в обратном порядке конструкторов. Вы можете положиться на это.

+0

Спасибо! Мне удалось понять это. –

+0

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

+1

@iheanyi: Чтобы решить проблему, как указано, вам нужно. В противном случае 'Inner' не будет иметь инициализированный элемент данных. Кроме того, мои классы довольно часто не имеют конструктора по умолчанию. Это потому, что для многих моих классов состояние инициализированного, но не заполненного данных не имеет никакого смысла. YMMV, но добавление конструкторов по умолчанию, чтобы избежать перечисления элементов данных в списке инициализации, безусловно, кажется мне очень неправильным советом. – sbi

1

Поскольку Inner не имеет конструктора по умолчанию, вам необходимо его инициализировать явно. Способ сделать это, как отметил @sbi, использует список инициализации в конструкторе.

+0

Или он мог бы создать конструктор по умолчанию. – iheanyi

-1

Вы также можете добавить конструктор к Inner, который не принимает никаких аргументов. Это неявно пытается вызвать его, поскольку вы явно не инициализируете in в Outer. Если in были указателем (Inner *in), то это сработало бы.

В принципе, если вы пишете

Foo f; 

в C++, он будет вызывать конструктор по умолчанию (Foo::Foo()).

+0

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

+0

О, это правда. просто хотел указать, что это также возможно (и в общих чертах может потребоваться). –

+0

+1 @sbi Возможно, в этом конкретном случае это было бы. Но, как правило, использование конструктора по умолчанию является вопросом целесообразности, не обязательно плохим. – iheanyi

Смежные вопросы