Дизайн иконок pimpl против схемы моста

Я только что заметил новый термин pimpl idiom, какова разница между этой идиомой с шаблоном проектирования Bridge? Я смущен этим.

Я также заметил, что идиома pimpl всегда используется для функции подкачки, что это? Может ли кто-нибудь привести мне пример?

Ответ 1

PIMPL - это способ скрытия реализации, в первую очередь для разрыва зависимостей компиляции.

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

swap - стандартная функция С++ для обмена значениями двух объектов. Если вы поменяете указатель на реализацию для другой реализации, вы существенно измените механизм класса во время выполнения.

Но в своей базовой и общей форме класс с использованием PIMPL указывает на одну реализацию, поэтому нет абстрактного класса с различными подклассами - всего один класс, объявленный вперед и скомпилированный в другом месте. Изменение класса реализации не требует перекомпиляции источников, которые включают основной заголовок.

Например, скажем, у вас много частных функций-членов, частных перечислений и личных данных. И эти частные "биты" меняются довольно часто, когда класс развивается и поддерживается. Если зависимости #include таковы, что прикосновение к этому заголовочному файлу приводит к перекомпиляции большого количества источников, у вас есть хороший кандидат для PIMPL.

Итак, шаблон Bridge относится к объектно-ориентированному дизайну, а идиома PIMPL - о физическом дизайне файлов.

(Для получения дополнительной информации о физическом дизайне я рекомендую книгу крупномасштабный дизайн программного обеспечения на С++ от John Lakos.)

Ответ 2

Я использовал указатель impl, чтобы скрыть детали реализации из файла public interface/include В основном интерфейс выглядит следующим образом:

struct Interface {
private:
class Impl;
Impl *pImpl;
};

Затем где-то внутри Interface::Impl определяется и реализуется, но детали не отображаются.

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

Ответ 3

Pimpl: Короче говоря, шаблон pimpl действительно хорош для скрытия частной реализации. Если вы хотите разоблачить файлы заголовков для клиентов, которые должны были создаваться против вашего открытого интерфейса, но вы не хотели раскрывать свои данные о частной реализации, вы могли бы использовать шаблон pimpl, чтобы скрыть детали. Вы сделали бы это по нескольким причинам, но главным образом ваш заголовочный файл не изменился бы при изменении изменения частной информации о реализации, которое в противном случае заставило бы ваших клиентов перекомпилировать. Таким образом, вы можете скрыть свои личные детали реализации. Обычно вы должны держать указатель impl в контейнере RAII, как указатель unqiue, чтобы убедиться, что он освобожден после уничтожения.

Pimpl

    // my_class.h
    class my_class {
       public:
        // public here
    private:
       class impl;  //forward declare impl
       unique_ptr<impl> pimpl; // private implementation pointer
    };


    // my_class.cpp
    class my_class::impl {  // defined privately here
      // ... all private data and functions: all of these
      //     can now change without recompiling callers ...
    };
    my_class::my_class(): pimpl( new impl )
    {
      // ... set impl values ... 

       my_class& operator=(my_class other);
       friend void swap (my_class& lhs, myclass& rhs);  // used for assignment
    }

Swap, вероятно, возник из-за того, что, когда вы выполняете оператор присваивания для этого класса и реализуете свою собственную процедуру подкачки для назначения членов, вам также нужно знать о назначении этого указателя

    my_class& my_class::operator=(my_class other)
    {
      swap(*this,other);
      return  *this;
    }

    void swap ( my_class& lhs, myclass& rhs )
    {
      using std::swap // now use default swap if override doesn't exist

      // call swap for other members
     //  swap (lhs.data,rhs.data);

      // call swap on unique_ptr
      lhs.pimpl.swap(rhs.pimpl);  // doesn't throw exceptions

    }

Мост - это совершенно отдельный шаблон, который используется для объединения элементов вместе. Сходство между шаблонами заключается в том, что у вас может быть метод процесса в вашем классе, который скрывает фактический вызов, поскольку он делегирует этот вызов содержащемуся объекту, который будет обрабатывать вызов фактического метода. Другими словами, если у вас есть базовый класс интерфейса запроса, который содержит указатель на базовый класс-реализатор запроса. Поэтому во время выполнения вы можете "мостить" системы, инициализируя мост конкретным типом реализации запроса, но кто-то, вызывающий мост, просто вызовет метод запроса процесса, который делегирует вызов определенному методу запроса производного метода реализации во время выполнения. В Google есть несколько источников с хорошими диаграммами, которые также могут объяснить это более четко.

Ответ 5

В настоящее время просматривается библиотека Boost, которая реализует шаблон Pimpl. Я объединил несколько базовых примеров, используя предлагаемую реализацию Boost Pimpl, если кто-то захочет воспользоваться этим внутренним кодом:

https://github.com/sean-/Boost.Examples/tree/a148be39abcb21428857aa50495f8c352600741e/pimpl


UPDATE: приведенная выше ссылка была обновлена, чтобы указать на архивированную версию. Не кажется вероятным, что Boost.Pimpl будет принят для повышения в этот момент времени в пользу std::unique_ptr<> как жизнеспособной замены (хотя и менее полной, чем Boost.Pimpl для некоторых менее распространенных случаев).

Если у вас есть доступ к C++11, вам, вероятно, лучше использовать std::unique_ptr<> в качестве реализации PIMPL:

class MyClass {
 public:
  // ...
 private:
  class Impl;
  std::unique_ptr<Impl> impl_;
};

Вы можете увидеть полный пример, используя std::unique_ptr<> в моей стратегии С++ 11 reentrant lock locking.

Используя метод std::unique_ptr<> swap(), для семантики перемещения C++11 становится удобной и очень практичной, чтобы перемещать кишки объекта от одного владельца к другому. Например, предположим, что MyClass экспортирует реализацию swap(), которая просто переходит в std::unique_ptr<> swap:

void MyClass::swap(MyClass *c) { impl_.swap(c); }
MyClass c1, c2;
c1.swap(c2);

Теперь это безопасный способ исключения содержимого c2 для c1. Другой большой причиной использования идиомы PIMPL является сохранение/поддержание стабильного ABI.