Должен конструктор C++, который взаимодействует через интерфейс с аппаратными средствами, делают реальную работу? [дубликат]

Возможный дубликат:
Сколько работы должно быть сделано в конструкторе?

Я - strugging с некоторым советом, который я имею подсознательно, но за который я не могу помнить обоснование.

Я, кажется, не забываю в какой-то момент читать, некоторый совет (не может помнить источник), что конструкторы C++ не должны делать реальной работы. Скорее они должны инициализировать переменные только. Совет продолжал объяснять, что реальная работа должна быть сделана в своего рода init () метод, чтобы быть названной отдельно после того, как экземпляр был создан.

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

Существует ли серьезное основание, почему конструкторы не должны делать реальной работы? Очевидно, это могло замедлить время выделения, но это не будет несколько отличаться при вызове отдельного метода сразу после выделения.

Просто пытаясь выяснить, что глюки I не в настоящее время рассмотрение, которое могло бы иметь вывод к такому совету.

29
задан Jim Hunziker 23 July 2018 в 16:09
поделиться

10 ответов

Я помню, что Скотт Мейерс из «More Effective C ++» не рекомендует использовать лишний конструктор по умолчанию. В этой статье он также коснулся использования таких методов, как Init (), для «создания» объектов. По сути, вы ввели дополнительный шаг, который возлагает ответственность на клиента класса. Кроме того, если вы хотите создать массив указанных объектов, каждый из них должен будет вручную вызвать Init (). У вас может быть функция Init, которую конструктор может вызывать внутри для поддержания порядка в коде, или для вызова объекта, если вы реализуете Reset (), но по опыту лучше удалить объект и воссоздать его, чем пытаться сбросить его значения по умолчанию, если только объекты не создаются и не уничтожаются много раз в реальном времени (скажем, эффекты частиц).

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

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

26
ответ дан 28 November 2019 в 01:09
поделиться

Единственная реальная причина - это возможность тестирования. Если ваши конструкторы полны «реальной работы», это обычно означает, что объекты могут быть созданы только в полностью инициализированном, работающем приложении. Это признак того, что объект / класс нуждается в дальнейшей декомпозиции.

3
ответ дан 28 November 2019 в 01:09
поделиться

Стоит рассмотреть вопросы времени жизни и подключения/переподключения, как указывает Neal S..

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

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

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

2
ответ дан 28 November 2019 в 01:09
поделиться

Я хотел бы добавить сюда свой собственный опыт.

Я не буду много говорить о традиционных дебатах Конструктор / Инициализация ... например, Руководства Google не рекомендуют ничего, что есть в Конструкторе, но это потому, что они не рекомендуют Исключения и 2 работают вместе.

Я могу говорить о классе Connection , который я использую.

Когда создается класс Connection , он пытается на самом деле подключиться (по крайней мере, если не создан по умолчанию). Если Соединение не удается ... объект все еще создается, и вы об этом не знаете.

Когда вы пытаетесь использовать класс Connection , вы попадаете в один из трех случаев:

  • ни один параметр никогда не уточнялся> исключение или код ошибки
  • объект действительно подключен> нормально
  • объект не подключен, он попытается подключиться> это успешно, хорошо, это не удается, вы получаете исключение или код ошибки

Я думаю, что очень полезно иметь и то, и другое. Однако это означает, что в каждом методе, фактически использующем соединение, необходимо проверить, работает ли оно.

Это того стоит из-за событий отключения. Когда вы подключены, вы можете потерять соединение, даже если объект не узнает об этом. Инкапсулируя самопроверку соединения в метод reconnect , который вызывается изнутри всеми методами, требующими рабочего соединения, вы действительно изолируете разработчиков от решения проблем ... или, по крайней мере, настолько, насколько это возможно. поскольку, когда все терпит неудачу, у вас нет другого решения, которое сообщило бы им об этом :)

1
ответ дан 28 November 2019 в 01:09
поделиться

Выполнения "реальной работы" в конструкторе лучше избегать.

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

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

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

0
ответ дан 28 November 2019 в 01:09
поделиться

При использовании конструктора и метода Init () возникает источник ошибки. По моему опыту, вы столкнетесь с ситуацией, когда кто-то забудет позвонить ему, и у вас может оказаться незаметная ошибка. Я бы сказал, что вам не нужно много работать в своем конструкторе, но если нужен какой-либо метод инициализации, то у вас есть нетривиальный сценарий построения, и пора взглянуть на шаблоны создания. Разумно взглянуть на строительную функцию или фабрику. С помощью частного конструктора убедитесь, что никто, кроме вашей фабрики или функции-строителя, на самом деле не создает объекты, поэтому вы можете быть уверены, что он всегда строится правильно.

Если ваш дизайн допускает ошибки при реализации, кто-то их допустит. Мой друг Мерфи сказал мне об этом;)

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

3
ответ дан 28 November 2019 в 01:09
поделиться

Есть пара причин, по которым я бы использовал отдельный конструктор / init ():

  • Ленивая / отложенная инициализация. Это позволяет быстро создавать объект, быстро реагировать на запросы пользователя и откладывать более длительную инициализацию для более поздней или фоновой обработки. Это также часть одного или нескольких шаблонов проектирования, касающихся многоразовых пулов объектов, чтобы избежать дорогостоящего выделения.
  • Не уверен, есть ли у него собственное имя, но, возможно, когда объект создается, информация об инициализации недоступна или не понимается тем, кто создает объект (например, массовое создание универсального объекта). В другом разделе кода есть ноу-хау для его инициализации, но не для его создания.
  • По личной причине деструктор должен иметь возможность отменять все, что делал конструктор. Если это связано с использованием внутреннего init / deinit (), нет проблем, если они являются зеркальным отображением друг друга.
0
ответ дан 28 November 2019 в 01:09
поделиться

Единственная причина, по которой нельзя выполнять "работу" в конструкторе, заключается в том, что если оттуда выбрасывается исключение, деструктор класса не вызывается. Но если вы используете принципы RAII и не полагаетесь на свой деструктор при очистке, я считаю, что лучше не вводить метод, который не требуется.

18
ответ дан 28 November 2019 в 01:09
поделиться

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

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

Вы должны задать себе следующий вопрос:

Можно ли использовать объект без вызова метода init ?

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

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

9
ответ дан 28 November 2019 в 01:09
поделиться

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

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

Когда я реализовал классы, которые взаимодействуют с внешним оборудованием, я обнаружил, что проще создать экземпляр «отключенного» объекта и предоставить методы для подключения и установки начального состояния. Это обычно обеспечивает большую гибкость при подключении / отключении / повторном подключении к устройству.

7
ответ дан 28 November 2019 в 01:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: