Вызов метода в конструкторе
Херб Саттер упоминает в одной из своих статей http://www.gotw.ca, что объект построен (имеет действительное существование), только если конструктор выполняет завершение. т.е. грубо контролировать контроль за его конечной фигурной скобкой.
Теперь рассмотрим следующий код
class A
{
public:
A()
{
f();
}
void f()
{
cout << "hello, world";
}
};
int main()
{
A a;
}
Теперь из того, что говорит Херб, нельзя сказать, что поскольку A не полностью сконструирован внутри своего конструктора Вызов f() внутри конструктора недопустим, поскольку ptr еще не готов.
Тем не менее в конструкторе действительно существует "this", и f() действительно вызвано.
Я не думаю, что Херб говорит что-то неправильное... но думаю, что я неправильно его интерпретирую... может кто-нибудь объяснить мне, что это такое?
Вот ссылка на статью: http://www.gotw.ca/gotw/066.htm
В нем говорится об исключениях от конструкторов. В частности, вот выдержка из него, на которой основан мой вопрос:
-Когда начинается срок жизни объекта?
Когда его конструктор успешно завершен и обычно возвращается. То есть, управление достигает конца тела конструктора или более раннего оператора return.
-Когда конец жизни объекта заканчивается?
Когда начнется его деструктор. То есть, управление достигает начала тела деструктора.
Важным моментом здесь является то, что состояние объекта до его начала жизни точно такое же, как и после его окончания жизни - нет объекта, периода. Это наблюдение приводит нас к ключевому вопросу:
Мы можем суммировать модель конструктора С++ следующим образом:
Either:
(a) The constructor returns normally by reaching its end or a return statement, and the object exists.
Or:
(b) The constructor exits by emitting an exception, and the object not only does not now exist, but never existed.
Ответы
Ответ 1
Теперь, что говорит Херб, нельзя сказать, что что поскольку A не полностью построенный внутри его конструктора Вызов f() внутри конструктора недействителен, поскольку "this" ptr не готов пока.
Это только тогда, когда f()
является virtual
методом class A
или его иерархией наследования, и вы ожидаете разрешения времени выполнения для f()
в соответствии с правильным объектом. Простыми словами механизм virtual
не срабатывает, если метод вызывается внутри конструктора.
Если f()
не является виртуальной функцией, нет никакого вреда в вызове его из конструктора (ов), если вы знаете, что именно делает f()
. Программисты обычно называют методы класса, такие как initialize()
из конструктора (ов).
Можете ли вы дать мне ссылку на статью Herb Sutter?
Ответ 2
К тому времени, когда поток программы входит в ваш конструктор, память объекта была выделена и указатель this
действительно действителен.
Что означает Herb, заключается в том, что состояние объекта может не полностью инициализироваться. В частности, если вы строите класс, полученный из A
, тогда конструктор этого класса не будет вызываться, пока вы все еще находитесь внутри конструктора A.
Это важно, если у вас есть виртуальные функции-члены, поскольку любая виртуальная функция в производном классе не будет запущена, если вызвана изнутри конструктора A.
Ответ 3
Импликация из времени жизни, не начатого еще, в основном состоит в том, что, если конструктор генерирует исключение, деструктор не будет запущен.
Ответ 4
Примечание: это было бы проще с точной статьей, так что мы могли бы иметь некоторый контекст
Пожизненные соображения на самом деле довольно сложны.
Учитывая конструктор объекта, существуют две разные точки зрения:
- внешний: т.е. пользователь объекта
- internal: т.е. вы при написании конструкторов и деструкторов (особенно)
С внешней точки зрения время жизни объекта:
- начинается после успешного завершения конструктора
- заканчивается, когда деструктор начинает работать
Это означает, что если вы попытаетесь получить доступ к средству средней или средней разрушения объекта Bad Things Happen (tm). Это в основном относится к многопоточным программам, но может случиться, если вы передадите указатели на свой объект на базовые классы... что приводит к...
... внутренняя точка зрения. Это сложнее. Единственное, что вы уверены в том, что выделена требуемая память, однако части объектов еще не могут быть полностью инициализированы (в конце концов, вы ее создаете).
- в теле конструктора вы можете использовать атрибуты и базы класса (они инициализированы), а функции вызова обычно (виртуальные вызовы следует избегать).
- если это базовый класс, производный объект еще не инициализирован (таким образом, ограничение на виртуальные вызовы)
Ответ 5
Остерегайтесь переменных-членов, которые еще не инициализированы. Остерегайтесь виртуальных функций: функция, которую вы вызываете, может быть не той, которую вы ожидаете, если функция является виртуальной и создается производный объект. Кроме этого, я не вижу проблем с вызовом методов из конструктора. Особенно память для объекта уже выделена.