Как вы заметили, спецификатор доступа работает на основе типа указателя, а не на основе динамического типа. Таким образом, указание частного в B в этом случае просто означает, что функции не могут быть доступны через указатель на B. Таким образом, это может быть полезно для хранения вещей в заблокированных случаях, когда клиент не должен использовать указатели на B (или создавать B в стеке). В основном, в тех случаях, когда конструктор B является закрытым, и вы создаете B (и, возможно, других детей из A) через фабрику, которая возвращает unique_ptr<A>
. В этом случае вы можете просто указать все методы B как частные. В принципе это не позволяет клиенту «злоупотреблять» интерфейсом путем динамического литья unique_ptr вниз, а затем напрямую обращаться к интерфейсу B.
Я действительно не думаю, что это должно быть сделано; это более Java-y-подход, чем C++. В общем случае, если клиент хочет использовать производный объект в стеке, а не в куче через указатель базового класса, они должны быть в состоянии. Это дает лучшую производительность, и это легче рассуждать. Он также лучше работает в общем коде.
Редактировать: Я думаю, что я должен уточнить. Рассмотрим следующий код:
enum class Impl {FIRST, SECOND, THIRD};
unique_ptr<A> create(Impl i) {
...
}
Предположим, что это единственный способ создать конкретные экземпляры, которые используют интерфейс A. Я мог бы желать, возможно, что производные классы являются чистыми деталями реализации. Например, я мог бы реализовать каждую из трех реализаций в другом классе, а затем решить, что два из трех могут быть объединены в один класс с разными параметрами и т. Д. Это не бизнес пользователя; их мир - это просто интерфейс A и функция create
. Но теперь предположим, что пользователь бывает смотреть на источник и знает, что FIRST
реализация реализуется с помощью B. Они хотят более высокую производительность, так что они делают это:
auto a = create(Impl::FIRST);
auto b = dynamic_cast<B *>(a.get());
// Use b below, potentially avoiding vtable
Если пользователь имеет код, как это, и вы избавляете или переименуйте класс B, их код сломается. Сделав все частные методы B, вы делаете b-указатель бесполезным для пользователя, гарантируя, что он использует интерфейс по назначению.
Как я уже говорил, я не особо защищаю это программирование на C++. Но могут быть ситуации, когда вам действительно нужны производные классы, чтобы быть чистыми деталями реализации; в тех случаях, когда изменение спецификатора доступа может помочь обеспечить его выполнение.
Как в стороне, модификатор 'public' в структуре A ничего не делает, потому что все в структуре открыто по умолчанию. – MrEricSir
Лично я бы не рекомендовал менять уровень доступа, поскольку я думаю, что это может ввести в заблуждение. Однако я не знаю причин, почему это будет считаться небезопасным, и я не знаю, почему это было бы полезно. Связанный, может быть полезно иметь частную виртуальную функцию, вызываемую не виртуальной публичной функцией. См. Идиому NVI (не виртуального интерфейса). –
@JamesAdkison Это «небезопасно», потому что позволяет внешнему коду вызвать частную функцию напрямую, что противоречит всей точке частных функций. – immibis