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

С++ - построение объекта внутри класса

Я новичок в С++, и я не уверен в этом. Взгляните на следующий пример, который подводит итог моей текущей проблеме.

class Foo
{
    //stuff
};

class Bar
{
    Foo foo;
};

Таким образом, Bar создает полный объект Foo, а не только ссылку или указатель. Является ли этот объект инициализирован его конструктором по умолчанию? Мне нужно явно вызвать его конструктор, и если да, то как и где?

Спасибо.

4b9b3361

Ответ 1

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

class Foo
{
    public: 
    Foo(int val) { }
    //stuff
};

class Bar
{
    public:
    Bar() : foo(2) { }

    Foo foo;
};

Ответ 2

Строительство - довольно сложная тема в С++. Это простой ответ. Игнорируется ли Foo или нет, зависит от определения самого Foo. О втором вопросе: как заставить Bar инициализировать Foo: списки инициализации - это ответ.

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

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

class Foo {
   int x;
public:
   void dump() { std::cout << x << std::endl; }
   void set() { x = 5; }
};
class Bar {
   Foo x;
public:
   void dump() { x.dump(); }
   void set() { x.set(); } 
};
class Bar2
{
   Foo x;
public:
   Bar2() : Foo() {}
   void dump() { x.dump(); }
   void set() { x.set(); }
};
template <typename T>
void test_internal() {
   T x;
   x.dump();
   x.set();
   x.dump();
}
template <typename T>
void test() {
   test_internal<T>();
   test_internal<T>();
}
int main()
{
   test<Foo>(); // prints ??, 5, 5, 5, where ?? is a random number, possibly 0
   test<Bar>(); // prints ??, 5, 5, 5
   test<Bar2>(); // prints 0, 5, 0, 5
}

Теперь, если Foo имеет определяемый пользователем конструктор, он будет инициализирован всегда, независимо от того, имеет ли Bar или нет инициализируемый пользователем конструктор. Если Bar имеет пользовательский конструктор, который явно вызывает (возможно неявно определенный) конструктор Foo, то Foo фактически будет инициализирован. Если список инициализации Bar не вызывает конструктор Foo, то он будет эквивалентен случаю, когда Bar не имеет определенного пользователем конструктора.

Для проверки кода может потребоваться некоторое объяснение. Нам интересно, инициализирует ли компилятор переменную без кода пользователя, фактически вызывающего конструктор. Мы хотим проверить, инициализирован ли объект или нет. Теперь, если мы просто создадим объект в функции, это может привести к удалению позиции памяти, которая была не затронута и уже содержит нули. Мы хотим отличить удачу от успеха, поэтому мы определяем переменную в функции и дважды вызываем ее. В первом запуске он распечатает содержимое памяти и принудительно изменит ее. Во втором вызове функции, поскольку трассировка стека одинакова, переменная будет находиться в точно такой же позиции памяти. Если он был инициализирован, он будет установлен в 0, иначе он сохранит то же значение, что и старая переменная в точно такой же позиции.

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

Ответ 3

Существует четыре функции, которые компилятор С++ будет генерировать для каждого класса, если это возможно, и если вы не предоставите их: конструктор по умолчанию, конструктор копирования, оператор присваивания и деструктор. В стандарте С++ (глава 12, "Специальные функции" ) они называются "неявно объявленными" и "неявно определенными". Они будут иметь открытый доступ.

Не путайте "неявно определенный" с "по умолчанию" в конструкторе. Конструктор по умолчанию - это тот, который можно вызвать без каких-либо аргументов, если он есть. Если вы не создадите конструктор, то он будет определен неявно. Он будет использовать конструкторы по умолчанию для каждого базового класса и элемента данных.

Итак, происходит то, что класс Foo имеет неявно определенный конструктор по умолчанию, а Bar (который, как представляется, не имеет определяемого пользователем конструктора) использует свой неявно определенный конструктор по умолчанию, который вызывает конструктор по умолчанию Foo.

Если вы хотите написать конструктор для Bar, вы можете упомянуть foo в его списке инициализаторов, но поскольку вы используете конструктор по умолчанию, вам на самом деле не нужно его указывать.

Помните, что если вы пишете конструктор для Foo, компилятор не будет автоматически генерировать конструктор по умолчанию, поэтому вам нужно будет указать его, если вам это нужно. Поэтому, если вы хотите вставить что-то вроде Foo(int n); в определение Foo и явно не указали конструктор по умолчанию (либо Foo();, либо Foo(int n = 0);), вы не могли бы иметь Bar в его нынешнем виде, поскольку он не может использовать конструктор по умолчанию Foo. В этом случае вы должны иметь конструктор типа Bar(int n = 0): foo(n) {}, имеющий конструктор Bar, инициализирующий Foo. (Обратите внимание, что Bar(int n = 0) {foo = n;} или тому подобное не будет работать, поскольку конструктор Bar сначала попытается инициализировать foo, и это потерпит неудачу.)

Ответ 4

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

Bar::Bar() : foo(42) {}

Это, конечно, предполагает, что вы добавите Foo:: Foo (int) в код:)

Ответ 5

Таким образом, Bar создает полный объект Foo, а не только ссылку или указатель. Этот объект инициализирован его конструктором по умолчанию?

Если Foo имеет значение по умолчанию ctor, объект типа Foo будет использовать по умолчанию ctor при создании объекта типа Bar. В противном случае вам нужно вызвать Foo ctor самостоятельно, или ваш Bar ctor сделает ваш компилятор жалуется loduly.

например:

class Foo {
public:
 Foo(double x) {}
};

class Bar  {
 Foo x;
};

int main() {
 Bar b;
}

Вышеупомянутый компилятор будет жаловаться на что-то вроде:

"В конструкторе" Bar:: Bar() ': Строка 5: ошибка: нет соответствующей функции для вызова в Foo:: Foo()'

Мне нужно явно вызвать его конструктор, и если да, то как и где?

Измените приведенный выше пример следующим образом:

class Foo {
 public:
  Foo(double x) {} // non-trivial ctor
};

class Bar  {     
 Foo x;
public:
  Bar() : x(42.0) {} // non-default ctor, so public access specifier required
};

int main() {
 Bar b;
}

Ответ 6

Полный объект. Нет, он по умолчанию построен в стандартном конструкторе Bar.

Теперь, если у Foo был конструктор, который взял, скажем, int. Вам понадобится конструктор в Bar для вызова конструктора Foo и скажите, что это такое:

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

class Bar {
public:
    Bar() : foo(42) {}

    Foo foo;
};

Но если у Foo был конструктор по умолчанию Foo(), компилятор автоматически генерирует конструктор Bar, и это будет вызывать Foo по умолчанию (то есть Foo())

Ответ 7

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

Bar::Bar( int baz ) : foo( baz )
{
    // Rest of the code for Bar::Bar( int ) goes here...
}

Ответ 8

Вам не нужно явно вызывать конструктор по умолчанию на С++, он будет вызван для вас. Если вы хотите вызвать другого конструктора, вы можете сделать это:

Foo foo(somearg)