Инициировать или построить - PullRequest
5 голосов
/ 05 октября 2009

Я рассматриваю некоторый код и вижу много этого:

class Foo
{
public:
  Foo()
  {
    // 'nuffin
  }

  void init()
  {
    // actual construction code
  }
} ;

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

Это хорошая идея или плохая идея?

Ответы [ 10 ]

4 голосов
/ 05 октября 2009

Мне это не нравится. Мне кажется, что после строительства объект должен быть ... хорошо ... построен. Этот код оставляет его в недопустимом состоянии, что почти 1 никогда не бывает хорошим.

1 Ласковое слово вставлено для учета непредвиденных обстоятельств.

3 голосов
/ 05 октября 2009

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

Если у вас есть отдельная подпрограмма init (), которая должна быть вызвана до того, как что-либо еще сработает, вызывающие абоненты с большей вероятностью будут проверять код возврата , чем вызывать метод didInitializationSucceed() после создания объекта.

2 голосов
/ 05 октября 2009

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

Требовать от потребителей вашего объекта не забывать звонить init() - плохая идея, потому что они этого не сделают.

1 голос
/ 05 октября 2009

Я использую contructor и init, если создаю экземпляры объектов, основанных на вызове базы данных. Итак, если мне нужен пустой объект, чтобы я мог заполнить его и затем сохранить в базе данных, я создаю без параметров и не вызываю init (). Принимая во внимание, что если мне нужно будет извлечь элементы объекта из БД, я contruct($param) передам $ param в init($param).

1 голос
/ 05 октября 2009

В некоторых языках (читай: C ++) вы не можете вызывать конструктор из другого конструктора, поэтому, если вам нужна общая часть нескольких конструкторов, вам нужно поместить ее в отдельный метод, и я видел имя init () используется для этого. Но это не то, о чем ты говоришь?

1 голос
/ 05 октября 2009

Один из случаев, когда это может применяться, - это когда Foo является атрибутом другого класса и не может быть полностью построен до того, как будет создан родительский класс. Только тогда «Foo» может быть «заполнен».

1 голос
/ 05 октября 2009

Я полагаю, что конструктор должен также выполнять часть init (). Если объект полностью не построен, его не следует использовать.

Кроме того, инициализация в конструкторе позволяет использовать RAII. Основная задача RAII - представить ресурс локальным объектом, инициализировать в конструкторе, чтобы деструктор локального объекта освободил ресурс. Таким образом, программист не может забыть освободить ресурс.

0 голосов
/ 05 октября 2009

Хотя это не может считаться нормальным или предпочтительным способом конструирования объектов, при некоторых обстоятельствах это может быть путь. Например, вам может понадобиться сконструировать объект просто для указания его существования (в каком-то списке, где значение имеет значение и т. Д.), Но инициировать его позже, только если этот конкретный объект используется впервые, так как инициализация всей коллекции объектов будет займет много времени.

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

0 голосов
/ 05 октября 2009

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

0 голосов
/ 05 октября 2009

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

Однако могут существовать некоторые семантические контексты, в которых может иметь смысл доставлять неинициализированные объекты - например, если контекст требует, чтобы в контейнере были объекты, но вы не хотите их инициализировать позже, потому что инициализация медленно и / или, возможно, объекты не нужны. В этих случаях дополнительная сложность может сделать что-то еще проще.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...