чем можно пожертвовать с помощью этой альтернативы pimpl?

#c

#c

Вопрос:

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

 // .hpp file

struct A
{
private:
  struct B;
  int a{};
};

// .cpp file
struct A::B
{
  // replaces a private member function
  static void f(Aamp; a)
  {
      a.a;
  }
};
  

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

Комментарии:

1. «Мне не нравится Qt. Qt использует PImpl. Поэтому мне не нравится Pimpl.» не является допустимым аргументом.

2. Вам нужно создать экземпляр A::B , чтобы сделать с ним что-нибудь полезное. Где вы это делаете?

3. Это не обеспечивает поведение класса A , поэтому оно ни в каком смысле не является альтернативой pimpl.

4. Это может быть, поскольку использование Qt означает необходимость перебирать тысячи строк его кода.

5. @user1095108: Тогда ваш аргумент «Прочитав тысячи строк кода Qt, использующего pimpl, я возненавидел pimpl»?

Ответ №1:

Ну, во-первых, это не сработает, если struct B должен быть класс template , который принимает параметры произвольного типа.

Ответ №2:

Если содержит только статические методы / данные, нет смысла вообще указывать это в заголовке. A::B Вы можете гораздо проще реализовать только эти функции в .cpp файле, не делая их частью класса.

 // .hpp file

struct A
{
};

// .cpp file
void f(Aamp; a)
{
  // some function
  // Might be called from the implementation of methods of `A`
}
  

Таким образом, на самом деле нет причин иметь вложенный класс в первую очередь.

С другой стороны, если у вас есть нестатические данные / …, A::B которые должны быть связаны с экземплярами A (ситуация, когда используется идиома pimpl), тогда вам каким-то образом нужно связать A::B экземпляр с каждым экземпляром A .

Простым способом сделать это было бы сохранить указатель на a A::B в каждом A . Это идиома pimpl.

Комментарии:

1. Вы забываете, что как вложенный класс B является «другом» A. Он может получить доступ ко всем частным / защищенным членам. Но нет, если вы объявите его вне A.

Ответ №3:

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

Ваш код этого не делает, поэтому он не заменяет pimpl.

Короче говоря, ваш код — это способ скрыть вспомогательные функции, которые являются деталями реализации. pimpl делает это, а ТАКЖЕ скрывает вспомогательные данные, которые являются деталями реализации.

Комментарии:

1. но B может работать с членами A , а члены могут быть частными, поэтому не являются частью общедоступного интерфейса.

2. @user1095108: я не вижу никаких членов A для работы. Добавьте немного, и вы увидите, насколько это отличается от pimpl.

3. @user1095108: An int ? Действительно? Неудивительно, что вам не нравится pimpl, вы не имеете ни малейшего представления о том, когда его следует использовать.

4. Итак, что плохого в наличии одного int члена? Я не должен писать много кода.

5. @user1095108: Подумайте о том, когда элемент является шаблонным контейнером STL, использующим пользовательский распределитель пула и хранящим другой сложный тип. Теперь вам нужно как минимум 3 дополнительных файла заголовков A.hpp , один для контейнера, один для распределителя, один для типа элемента. И все потребители A будут перестроены, если какие-либо из них изменятся. С помощью pimpl эти заголовки используются только реализацией A.