Указатели и объектно-ориентированное программирование

Как указатели работают с концепциями объектно-ориентированного программирования?

Насколько я понимаю (и, пожалуйста, признайте, что меня классифицируют как ID-10T), основным принципом ООП является сдерживание и сохранение ответственности за управление (память / реализация / и т. Д.), Содержащейся в классе; но когда метод объекта возвращает указатели, создается впечатление, что мы "выталкиваем" объект. Теперь кто-то может беспокоиться о:

  1. Они должны удалить объект, связанный с указателем?
  2. Но что, если классу все еще нужен объект?
  3. Могут ли они изменить объект? Если так, то как? (Я узнаю const может решить эту проблему)
  4. и так далее...

Кажется, что пользователь объекта теперь должен знать гораздо больше о том, как работает класс и что класс ожидает от пользователя. Это похоже на сценарий "кошка из сумки", которая, кажется, дает пощечину ООП.

ПРИМЕЧАНИЕ. Я заметил, что это не зависит от языка; Однако мне было предложено задать вопрос во время работы в среде C++.

3 ответа

Решение

То, что вы описываете, это проблемы собственности. Они ортогональны (то есть независимы, вы можете иметь либо без другого, либо даже оба) для ориентации объекта. У вас те же проблемы, если вы не используете ООП и манипулируете указателями на структуры POD. У вас нет проблемы, если вы используете ООП, но решаете ее как-то. Вы можете (попытаться) решить это, используя больше ООП или другим способом.

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

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

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

Для начала... Вы не можете иметь полиморфизм без указателей или ссылок. В C++ традиционно объекты копируются и имеют (по большей части) длительность автоматического хранения. Но копирование не работает с полиморфными объектами - они имеют тенденцию быть нарезанными. И ОО также часто означает идентичность, что, в свою очередь, означает, что вы не хотите копировать. Таким образом, решение заключается в том, чтобы объект был динамически размещен и передавал указатели. То, что вы делаете с ними, является частью дизайна:

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

Если объект является сущностью первого класса, а не логически частью другого объекта, то он, вероятно, должен позаботиться о себе сам. Опять же, другие объекты, которые могут содержать указатель на него, должны быть проинформированы, если он перестает существовать (или становится недействительным). Использование шаблона Observer является обычным решением. Когда я запускал C++, была мода на "управление отношениями", с какими-то классами управления, где вы регистрировали отношения, и которые якобы гарантировали, что все работает нормально. На практике они либо не работали, либо не делали ничего, кроме простого наблюдателя, и сегодня вы их больше не слышите.

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

Исходя из моего понимания и опыта, он обычно вращается вокруг того, что вы пытаетесь сделать, а также языка, использующего указатели (например, C++ против Objective-C).

Обычно в терминах C++ я обнаружил, что лучше всего возвращать ссылку на умный указатель (такой как std::shared_ptr) по ссылке (возможно, даже const ссылаться, в зависимости от ситуации), или просто скрыть указатель в классе, и если ему НУЖНО быть доступным или использоваться чем-то вне его, используйте метод getter, который либо копирует указатель и возвращает его, либо возвращает ссылку на указатель (предоставлен, AFAIK ref-to-ptr возможен только в C++). Если кто-то не знает, что вы не должны удалять ref-to-ptr в большинстве ситуаций (конечно, если его освобождение обрабатывается классом внутри), вам следует подумать дважды, готовы ли они заниматься делами C++ в своей команде.

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

Другие вопросы по тегам