Я имею:
class Foo;
class Bar {
Foo foo;
Bar(): foo(foo) {};
}
Bar bar;
На данном этапе
bar.foo // <--- how is this initialized?
[Этот вопрос явился результатом багги касательно - считаемая реализация указателя; я, возможно, поклялся, что удостоверился, что каждый указатель указывал на что-то непустое; но я закончил с указателем, который указал на что-то ПУСТОЕ.]
foo
полностью инициализируется, как только вы вошли в тело конструктора (это гарантированный общий случай; конкретно, как только он закончил инициализацию в списке initialize.)
В вашем случае, вы копируете-конструируете из неконструированного объекта. Это приводит к неопределенному поведению, согласно §12.7/1 (спасибо, gf):
Для объекта типа класса не-POD (пункт 9), до начала выполнения конструктора и после завершения выполнения деструктора, обращение к любому нестатическому члену или базовому классу объекта приводит к неопределенному поведению.
Фактически, приводится такой пример:
struct W { int j; };
struct X : public virtual W { };
struct Y {
int *p;
X x;
Y() : p(&x.j) // undefined, x is not yet constructed
{ }
};
Обратите внимание, компилятор не обязан давать диагностику неопределенного поведения, согласно §1.4/1. Хотя я думаю, мы все согласны, что это было бы неплохо, но это просто не то, о чем должны беспокоиться реализаторы компилятора.
Чарльз указывает на своего рода лазейку. Если Bar
имеет статическое хранение и если Foo
является POD-типом, то он будет инициализирован при выполнении этого кода. Переменные, хранящиеся в статическом хранилище, инициализируются до выполнения другой инициализации.
Это означает, что какой бы ни была Foo
, если для ее инициализации не требуется запуск конструктора (т.е. она является POD), ее члены будут инициализированы с нуля. По сути, вы копируете объект с нулевой инициализацией.
В целом, такого кода следует избегать. :)
.Это заманчиво, но в конечном счете, это зависит от ситуации.
Я не использовал его и лично не нахожу хорошего применения классическому образцу наследования до сих пор в своем опыте. Я переключился на программирование Javascript с Java, чтобы убежать от всех этих узоров дизайна!
Вы также можете (если вы еще не сделали этого) взглянуть на эти посты от Дугласа Крокфорда на то, что он думает о классическом наследстве в javascript.
http://www.crockford.com/javascript/inheritance.html
http://javascript.crockford.com/prototypal.html
-121--3181186-Bar(): foo(foo) {};
Это вызовет конструктор копирования foo
, таким образом копируя-конструируя из неинициализированного объекта. Это приведет к неопределенному поведению, за исключением того, что вы внедрили конструктор копирования, который обрабатывает этот конкретный случай, например:
class Foo
{
public:
Foo()
{
std::cout << "Foo()";
}
Foo(const Foo& from)
{
if(this == &from) std::cout << "special case";
else std::cout << "other case";
}
};
Но этот особый случай обычно используется для других целей, таких как дешевые копии последовательностей (при использовании строкового класса). Поэтому не пытайтесь использовать этот особый случай;)
Слегка расширенная версия вашего кода, кажется, указывает, что no, foo
никогда не инициализируется; казалось бы, у вас неопределенное поведение. В этом примере «Foo ()»
никогда не печатается, что означает, что экземпляр Foo
никогда не создавался:
#include <iostream>
class Foo {
public:
Foo() { std::cerr << "Foo()"; }
};
class Bar {
public:
Foo foo;
Bar(): foo(foo) {};
};
int main() {
Bar bar;
}
Разве Foo не использует внутренний конструктор по умолчанию, а список инициализации автоматически вызывает этот конструктор по умолчанию для инициализации объекта?