2008-11-06 9 views
19

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

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

+0

@Frederick, вы не «сиськи», это хороший вопрос! – jwfearn

+0

Хорошо заметили (каламбур)! –

ответ

10

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

Вы можете моделировать, однако, ваше желаемое поведение следующим:

class MyClass 
{ 
public: 
    // public stuff 

private: 
#include "MyClassPrivate.h" 
}; 

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

+0

Но, конечно же! Какая сиська я должен задать этот вопрос. Все равно спасибо всем. –

+0

Другой недостаток заключается в том, что изменение личного интерфейса по-прежнему требует перекомпиляции клиентов. – peterchen

+4

Почему этот ответ принят? «MyClassPrivate.h» так же легко читается как исходный заголовок. Он по-прежнему требует перекомпиляции. Размер объекта - незначительная проблема. Настоящие шоу-стопы - это эффективность и обратная совместимость с некоторыми идиомами C. – jfs

3

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

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

+0

Это не значит, что C++ поддерживает семантику значений ... Это то, что C++ будет фактически анализировать по значению, чтобы предоставить эти семантики (что является самым эффективным и простым в любом случае). – Arafangion

+0

@Ara: Не могли бы вы рассказать о термине «разобрать по значению»? Я никогда не слышал об этом. – fredoverflow

+0

@Fred: Ой, ты поймал мою опечатку - я делаю, инфакт, пропустил проход.:) – Arafangion

5

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

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

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

3

Да, но ...

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

6

Вы все игнорируя точку вопроса -

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

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

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

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

#pragma pimpl(MyClass_private.hpp) 

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

9

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

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

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

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

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

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

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

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

+0

Это не похоже на хорошую идею. Это хаки и похоже, что вы активно боретесь с языком, а не используете его! Неожиданно непрозрачные указатели на C выглядят как чистое и простое решение ... – dietr

 Смежные вопросы

  • Нет связанных вопросов^_^