Подтвердить что ты не робот

Почему частные члены/методы определены в интерфейсе?

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

Есть ли веская причина для этого, что я отсутствовал?

4b9b3361

Ответ 1

Для С++ это проблема с реализацией.

Компилятор С++ должен иметь возможность генерировать код, который использует класс, только видя объявление класса, а не реализацию. Одной очень важной задачей, которая необходима компилятору, является размер экземпляра класса, потому что, помимо прочего, С++ обрабатывает под-объекты в объектах путем вложения, а не путем хранения ссылки на отдельный объект. Чтобы иметь возможность построить объект (например, struct X { Y y; Z z; }), размер всех под-объектов (например, Y и Z) должен быть известен заранее.

Обходной путь для этой проблемы заключается в использовании шаблона "pimpl" (также называемого шаблоном "firewall" компилятора), который позволяет вам поддерживать все внутренние детали скрыты от пользователей класса. Это, к сожалению, несет в себе некоторую дополнительную стоимость, но в большинстве случаев это ничтожно мало. При таком подходе общедоступный объект всегда будет иметь размер указателя, и все данные в экземпляре будут доступны с помощью дополнительной косвенности... Преимущество состоит в том, что вы можете добавлять частные члены данных, а пользователям класса не нужно перекомпилировать (и если ваш класс, например, в DLL, это позволяет поддерживать даже двоичную совместимость).

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

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

Ответ 2

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

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

Я подозреваю, что вам также нужно публично объявлять не виртуальные функции на С++, чтобы поддерживать его целостность: в противном случае вам придется объявлять разные функции в разных местах, в зависимости от того, являются ли они общедоступными и/или виртуальными.

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

Ответ 3

Есть два смысла, в которых интерфейс слова используется в С++: интерфейс OOP и объявление типа.

Интерфейс OOP используется для инкапсуляции и полиморфизма. В С++ он обычно реализуется с чистыми абстрактными классами. PIMPL идиома также используется для инкапсуляции. В любом случае потребитель отображается только публичным членам типа и получает доступ к частной реализации через слой косвенности. Java и С# поддерживают явные интерфейсы и оба ограничивают доступ своих членов к общедоступному доступу.

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

Ответ 4

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

Это не противоречит идее инкапсуляции, частные методы/члены не являются частью PUBLIC-интерфейса, поэтому данные остаются инкапсулированными. Кроме того, публичные/частные модификаторы обеспечивают только видимость.

Ответ 5

В С++ существует множество трюков для решения этой проблемы.

Вы можете использовать factory pattern, чтобы скрыть частные члены и методы для пользователей. Вам нужно создать abstract class как ваш API или интерфейс, чем реализовать свой класс в подклассе и объявить все ваши частные члены там, таким образом, ваши пользователи не будут видеть ваших частных членов и реализации.

Ответ 6

Как вы делаете такой вывод? Я не нашел языков ООП, которые заставляют меня это делать. Для С++ причина, по которой люди определяют методы в интерфейсе, предназначена для наложения цели. Для членов, хорошо, потому что вы не можете определить его где-нибудь еще, и это применимо ко всем видимым, а не только частным.