Должен ли я полностью скрывать внутренний класс в моем заголовочном файле C++ при разработке моего SDK?

Я разрабатываю SDK, написанный на C++. У меня есть вопрос: могу ли я полностью скрыть внутренний класс в моем общедоступном заголовочном файле C++?

Фрагменты кода выглядят следующим образом (в заголовочном файле MyPublicClass.h):

namespace PublicNamespace
{
   namespace InternalNamespace
   {
      class MyInternalClass;
   }

   class MyPublicClass
   {
      public:
         void SomeMemberFunc();
         ...

      private:
         std::shared_ptr<InternalNamespace::MyInternalClass> mImpl;
   }
}

По шаблон проектирования C++ PImpl (а также многие другие материалы от Google), можно поместить InternalNamespace::MyInternalClass в общедоступный заголовок.

Моя мысль такова: кажется ненужным, чтобы внешние пользователи знали внутреннее пространство имен InternalNamespace, а также класс MyInternalClass. Поэтому я хочу использовать void для замены типа InternalNamespace::MyInternalClass.

То есть в моем случае я использую std::shared_ptr‹void› в качестве типа члена данных mImpl, а в .cpp, используйте std::static_pointer_cast‹InternalNamespace::MyInternalClass›(mImpl), чтобы преобразовать его в фактический класс. (Да, я знаю, что это преобразование стоит немного, но, пожалуйста, не обращайте на это внимания).

Является ли этот дизайн правильным или правильным? Спасибо всем.


person Bo Liu    schedule 28.06.2020    source источник
comment
Моя мысль такова: кажется ненужным сообщать внешним пользователям внутреннее пространство имен InternalNamespace, -- Почему вы думаете, что это выглядит ненужным? Многие, если не большинство библиотек C++, используют пространства имен и сообщают пользователю, какие элементы пространства имен находятся.   -  person PaulMcKenzie    schedule 28.06.2020
comment
Спасибо. Моя первоначальная мысль была такова: внутренний тип не может использоваться внешними пользователями, поэтому внешний пользователь не будет возражать, даже если не знает внутреннего типа.   -  person Bo Liu    schedule 28.06.2020


Ответы (1)


Не используйте void или void *, если нет абсолютно никакой альтернативы - использование указателей не позволяет компилятору обнаруживать ошибки во время компиляции и приводит к боли и страданиям.

Использование четко помеченного внутреннего пространства имен должно быть достаточно хорошим (предположим, что программисты, использующие ваш API, не ищут проблем преднамеренно, а если и так, у них в любом случае есть множество других способов найти их), хотя, если вы хотите полностью скрыть MyInternalClass от вызывающего кода, вместо этого вы можете сделать его внутренним классом MyPublicClass, т.е. что-то вроде этого:

namespace PublicNamespace
{
   class MyPublicClass
   {
      public:
         void SomeMemberFunc();
         ...

      private:
         class MyInternalClass 
         {
            [...]
         };

         std::shared_ptr<MyInternalClass> mImpl;
    }
}

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

person Jeremy Friesner    schedule 28.06.2020
comment
Спасибо, Джереми. Я не хочу, чтобы внешний пользователь знал внутренний тип, поэтому я упомянул в этой теме, чтобы скрыть. Так что, похоже, это хороший или правильный дизайн, чтобы внутренние типы по-прежнему присутствовали в моем общедоступном заголовочном файле SDK, верно? - person Bo Liu; 28.06.2020
comment
Если компилятор не позволяет пользователю ссылаться на внутренний класс в своем коде, это позволяет предотвратить зависимость пользовательского кода от существования внутреннего класса или деталей реализации; так что да, этого достаточно для цели. - person Jeremy Friesner; 28.06.2020