#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.