Вопрос

Как я понимаю, идиома pimpl существует только потому, что C ++ заставляет вас помещать все закрытые члены класса в заголовок. Если бы заголовок содержал только открытый интерфейс, теоретически, любое изменение в реализации класса не требовало бы перекомпиляции для остальной части программы.

Я хочу знать, почему C ++ не предназначен для такого удобства. Почему вообще требуется, чтобы закрытые части класса открыто отображались в заголовке (без каламбура)?

Это было полезно?

Решение

Это связано с размером объекта. Файл h используется, помимо прочего, для определения размера объекта. Если приватные члены не указаны в нем, то вы не узнаете, насколько велик объект для нового.

Однако вы можете смоделировать желаемое поведение следующим образом:

class MyClass
{
public:
   // public stuff

private:
#include "MyClassPrivate.h"
};

Это не навязывает поведение, но оно извлекает приватную информацию из файла .h. С другой стороны, это добавляет еще один файл для поддержки. Также в visual studio intellisense не работает для частных пользователей - это может быть плюсом или минусом.

Другие советы

Я думаю, что здесь есть путаница. Проблема не в заголовках. Заголовки ничего не делают (это всего лишь способы включить общие биты исходного текста в несколько файлов исходного кода).

Проблема в том, что она существует, заключается в том, что объявления классов в C ++ должны определять все, как общедоступное, так и приватное, что должен иметь экземпляр для работы. (То же самое относится и к Java, но из-за того, что ссылки на классы, скомпилированные извне, работают, нет необходимости использовать что-либо вроде общих заголовков.)

В сущности общих объектно-ориентированных технологий (а не только C ++) нужно, чтобы кто-то знал конкретный используемый класс и как использовать его конструктор для реализации, даже если вы используете только общественные части. Устройство в (3, ниже) скрывает это. Практика в (1, ниже) разделяет проблемы, независимо от того, делаете ли вы (3) или нет.

<Ол>
  • Используйте абстрактные классы, которые определяют только открытые части, главным образом методы, и пусть класс реализации наследуется от этого абстрактного класса. Таким образом, используя обычное соглашение для заголовков, существует abstract.hpp, который используется совместно. Существует также реализация .hpp, которая объявляет унаследованный класс и передается только тем модулям, которые реализуют методы реализации. В файле реализация.hpp будет #include " abstract.hpp " для использования в объявлении класса, которое он делает, так что существует единственная точка обслуживания для объявления абстрактного интерфейса.

  • Теперь, если вы хотите принудительно скрыть объявление класса реализации, у вас должен быть какой-то способ запросить конструкцию конкретного экземпляра, не имея специального полного объявления класса: вы не можете использовать new, и вы не может использовать локальные экземпляры. (Вы можете удалить, хотя.) Введение вспомогательных функций (включая методы в других классах, которые доставляют ссылки на экземпляры классов) является заменой.

  • Наряду с или как часть файла заголовка, который используется в качестве общего определения для абстрактного класса / интерфейса, включите сигнатуры функций для внешних вспомогательных функций. Эти функции должны быть реализованы в модулях, которые являются частью конкретных реализаций классов (чтобы они видели полное объявление класса и могли использовать конструктор). Сигнатура вспомогательной функции, вероятно, очень похожа на сигнатуру конструктора, но в результате она возвращает ссылку на экземпляр (этот прокси-конструктор может возвращать указатель NULL и даже может генерировать исключения, если вам нравится такая вещь). Вспомогательная функция создает конкретный экземпляр реализации и возвращает его в качестве ссылки на экземпляр абстрактного класса.

  • Миссия выполнена.

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

    Вы все игнорируете суть вопроса -

    Почему разработчик должен печатать код PIMPL?

    Для меня лучший ответ, который я могу придумать, заключается в том, что у нас нет хорошего способа выразить код C ++, который позволял бы вам работать с ним. Например, отражение времени компиляции (или препроцессора, или чего-то еще) или код DOM.

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

    Тогда вы могли бы написать что-то подобное в вашем общедоступном MyClass.h:

    #pragma pimpl(MyClass_private.hpp)
    

    А затем напишите свой собственный, действительно довольно тривиальный генератор обёрток.

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

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

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

    Может быть потому, что размер класса требуется при передаче его экземпляра по значениям, объединении его в другие классы и т. д.?

    Если бы C ++ не поддерживал семантику значений, это было бы хорошо, но это так.

    Да, но ...

    Вам необходимо прочитать «Дизайн и эволюция C ++» Страуструпа. книга. Это бы сдерживало внедрение C ++.

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top