шаблон pimpl против конструкции моста - PullRequest
29 голосов
/ 27 февраля 2010

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

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

Ответы [ 5 ]

49 голосов
/ 27 февраля 2010

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

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

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

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

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

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

(Подробнее о физическом дизайне я рекомендую книгу Джона Лакоса Крупномасштабный программный дизайн на C ++ )

3 голосов
/ 14 августа 2013

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 есть несколько источников с хорошими диаграммами, которые также могут объяснить это более четко.

3 голосов
/ 27 февраля 2010

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

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

Тогда где-то внутри Interface::Impl определено и реализовано, но детали не раскрыты.

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

1 голос
/ 01 июня 2011

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

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


ОБНОВЛЕНИЕ: ссылка выше была обновлена, чтобы указывать на архивную версию. Маловероятно, что 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<> в моем C ++ 11 стратегии блокирования реентерабельных классов question.

Используя метод 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.

1 голос
/ 27 февраля 2010

Ну, вот идиома PIMPL: http://en.wikipedia.org/wiki/Opaque_pointer Довольно ясно, что она делает.

И Bridge Pattern более вовлечен - он не просто хранит данные. http://en.wikipedia.org/wiki/Bridge_pattern#C.2B.2B

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...