Идиома Pimpl на практике

У меня возникает ошибка «Для этой операции должно быть указано значение PartitionKey» при запуске функции

blockquote>

В соответствии с этим сообщением об ошибке, я думаю, вам нужно установить partitionKey в вашем function.json файле. Пожалуйста, обратитесь к этому списку поддерживаемых свойств .

PartitionKey Определяет значение ключа раздела для поиска. Может включать параметры привязки.

blockquote>

Вот пример:

{
    "name": "inputDocument",
    "type": "documentDB",
    "databaseName": "MyDatabase",
    "collectionName": "MyCollection",
    "id" : "{queueTrigger}",
    "partitionKey": "{partition key value}",
    "connection": "MyAccount_COSMOSDB",
    "direction": "in"
}
47
задан Ryan Emerle 9 May 2009 в 14:10
поделиться

7 ответов

Я бы сказал то, делаете ли вы это для каждого класса или по принципу «все или ничего», зависит в первую очередь от того, почему вы выбираете идиому pimpl. Мои причины при построении библиотеки были следующие:

  • Хотел скрыть реализацию, чтобы избежать раскрытия информации (да, это не был проект FOSS :)
  • Хотел скрыть реализацию, чтобы сделать клиентский код менее зависим. Если вы создаете общую библиотеку (DLL), вы можете изменить свой класс pimpl, даже не перекомпилируя приложение.
  • Требуется сократить время, необходимое для компиляции классов с использованием библиотеки.
  • Требуется исправить конфликт пространств имен ( или похожие).

Ни одна из этих причин не требует подхода «все или ничего». В первом случае вы только упрощаете то, что хотите скрыть, тогда как во втором случае этого, вероятно, достаточно для классов, которые вы ожидаете изменить. Также по третьей и четвертой причинам есть только выгода от сокрытия нетривиальных элементов, которые, в свою очередь, требуют дополнительных заголовков (например, сторонней библиотеки или даже STL).

В любом случае, я хочу сказать, что я бы не стал Обычно такие вещи не кажутся слишком полезными:

class Point {
  public:      
    Point(double x, double y);
    Point(const Point& src);
    ~Point();
    Point& operator= (const Point& rhs);

    void setX(double x);
    void setY(double y);
    double getX() const;
    double getY() const;

  private:
    class PointImpl;
    PointImpl* pimpl;
}

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

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

В любом случае, я хочу сказать, что я бы не стал Обычно такие вещи не кажутся слишком полезными:

class Point {
  public:      
    Point(double x, double y);
    Point(const Point& src);
    ~Point();
    Point& operator= (const Point& rhs);

    void setX(double x);
    void setY(double y);
    double getX() const;
    double getY() const;

  private:
    class PointImpl;
    PointImpl* pimpl;
}

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

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

В любом случае, я хочу сказать, что я бы не стал Обычно такие вещи не кажутся слишком полезными:

class Point {
  public:      
    Point(double x, double y);
    Point(const Point& src);
    ~Point();
    Point& operator= (const Point& rhs);

    void setX(double x);
    void setY(double y);
    double getX() const;
    double getY() const;

  private:
    class PointImpl;
    PointImpl* pimpl;
}

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

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

В любом случае, я считаю, что я обычно не нахожу что-то подобное слишком полезно:

class Point {
  public:      
    Point(double x, double y);
    Point(const Point& src);
    ~Point();
    Point& operator= (const Point& rhs);

    void setX(double x);
    void setY(double y);
    double getX() const;
    double getY() const;

  private:
    class PointImpl;
    PointImpl* pimpl;
}

В этом случае вам начинает мешать компромисс, потому что нужно разыменовать указатель, а методы не могут быть встроены. Однако, если вы делаете это только для нетривиальных классов, небольшие накладные расходы обычно допускаются без каких-либо проблем.

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

В любом случае, я считаю, что я обычно не нахожу что-то подобное слишком полезно:

class Point {
  public:      
    Point(double x, double y);
    Point(const Point& src);
    ~Point();
    Point& operator= (const Point& rhs);

    void setX(double x);
    void setY(double y);
    double getX() const;
    double getY() const;

  private:
    class PointImpl;
    PointImpl* pimpl;
}

В этом случае вам начинает мешать компромисс, потому что нужно разыменовать указатель, а методы не могут быть встроены. Однако, если вы делаете это только для нетривиальных классов, небольшие накладные расходы обычно допускаются без каких-либо проблем.

36
ответ дан 26 November 2019 в 19:40
поделиться

Одно из самых больших применений pimpl ideom - создание стабильного C ++ ABI. Почти каждый класс Qt использует указатель «D», который является своего рода pimpl. Это позволяет выполнять гораздо более простые изменения без нарушения ABI.

22
ответ дан 26 November 2019 в 19:40
поделиться

Ясность кода

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

Ремонтопригодность

Для удобства обслуживания класса pimpl'd я лично обнаружил дополнительное разыменование при каждом доступе к данным - утомительно. Аксессоры не могут помочь, если данные являются чисто частными, потому что тогда вы все равно не должны предоставлять для них аксессор или мутатор, и вы застряли в постоянном разыменовании pimpl.

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

Производительность

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

Решение

Я думаю, что классы, в которых производительность имеет решающее значение, например, одно дополнительное разыменование или выделение памяти имеет существенное значение, не должны использовать pimpl, несмотря ни на что. Базовый класс, в котором это снижение производительности незначительно и в котором широко распространен файл заголовка # include'd, вероятно, должен использовать pimpl, если время компиляции значительно улучшится. Если время компиляции не сокращается, это зависит от вашего вкуса ясности кода.

Во всех остальных случаях это чисто дело вкуса. Попробуйте и измерьте производительность во время выполнения и во время компиляции, прежде чем принимать решение.

12
ответ дан 26 November 2019 в 19:40
поделиться

pImpl очень полезен, когда вы приходите к реализации std :: swap и operator = с надежной гарантией исключения. Я' m склонен сказать, что если ваш класс поддерживает любое из них и имеет более одного нетривиального поля, то обычно это больше не зависит от предпочтений.

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

Затраты на производительность, вероятно, больше связаны с потерей встраивания, чем они делать с косвенным обращением, но это дикая догадка.

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

Таким образом, ничто из этого не предполагает подход «все или ничего». Вы можете выборочно делать это для классов, где это приносит вам пользу, а не для тех, которые не приносят пользы, и изменить свое мнение позже. Реализация, например, итераторов как pImpl звучит как слишком много дизайна ...

7
ответ дан 26 November 2019 в 19:40
поделиться

Обычно я использую его, когда хочу, чтобы файл заголовка не загрязнял мою кодовую базу. Windows.h - прекрасный пример. С ним так плохо себя ведут, я лучше убью себя, чем его будет видно повсюду. Предполагая, что вам нужен API на основе классов, скрытие его за классом pimpl аккуратно решает проблему. (Если вы довольны тем, что просто предоставляете отдельные функции, их можно просто объявить вперед, конечно, не помещая их в класс pimpl)

Я бы не стал использовать pimpl везде , отчасти из-за снижение производительности, и отчасти потому, что требуется много дополнительной работы с обычно небольшой выгодой. Главное, что он дает, - это изоляция между реализацией и интерфейсом. Обычно это не очень высокий приоритет.

3
ответ дан 26 November 2019 в 19:40
поделиться

Эта идиома очень помогает при компиляции больших проектов.

Внешняя ссылка

Это тоже хорошо

5
ответ дан 26 November 2019 в 19:40
поделиться

Я использую идиому в нескольких местах в моих собственных библиотеках, в обоих случаях, чтобы четко отделить интерфейс от реализации. У меня, например, есть класс чтения XML, полностью объявленный в файле .h, который имеет PIMPL для класса RealXMLReader, который объявлен и определен в закрытых файлах .h и .cpp. RealXMlReader, в свою очередь, является удобной оболочкой для анализатора XML, который я использую (в настоящее время Expat).

Такое расположение позволяет мне в будущем перейти с Expat на другой XML-анализатор без необходимости перекомпилировать весь клиентский код (конечно, мне все равно нужно перекомпилировать).

Обратите внимание, что я делаю это не из соображений производительности во время компиляции, а только для удобства. Есть несколько фабнатиков PIMPL, которые настаивают на том, что любой проект, содержащий более трех файлов, будет некомпилированным, если вы не используете PIMPL повсюду. Это'

2
ответ дан 26 November 2019 в 19:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: