Совместимость с Pimpl и внутренними объектами без объявления друзей - PullRequest
4 голосов
/ 24 июня 2011

Я реализую несколько классов с использованием идиомы pimpl и сталкиваюсь с некоторыми проблемами при проектировании.

Во-первых, я всегда видел, как pimpl делается так:классы, использующие этот подход, и моя проблема заключается в том, что некоторым из этих классов требуется доступ к деталям реализации друг друга, но указатель _pimpl является частным.Очевидно, что если он публичный, то кто-то может случайно (или намеренно) переназначить его.(Я игнорирую тот факт, что «private» может быть определен как «public» как «public» и в любом случае предоставит доступ. Если вы сделаете это, то вы заслуживаете того, что получаете).и приветствовал бы любые комментарии в том же духе.

Мне очень не хочется использовать друзей, и я не уверен, что они даже помогут, так как вы не можете пересылать объявление Object :: ObjectImpl без полного определения Object.

то есть

 ...
 private:
    class ObjectImpl *_pimpl;
    friend class OtherObject::OtherObjectImpl; // this needs a fully qualified OtherObject
};

Thx Mark.

* ОБНОВЛЕНИЕ - Подробнее **

У меня есть два класса, одинназывается команда, другой называется результаты.У меня есть методы для Command, которые возвращают вектор Results.

И Command, и Results используют идиому pimpl.Я хочу, чтобы интерфейс результатов был как можно меньше.

class Command
{
public:
    void getResults( std::vector< Results > & results );
    void prepareResults( std::vector< Results > & results );
private:
    class CommandImpl *_pimpl;
};

class Results
{
public:
    class ResultsImpl;

    Results( ResultsImpl * pimpl ) :
        _pimpl( impl )
    {
    }

private
    ResultsImpl *_pimpl;
};

Теперь в Command :: getResults ().Я вставляю ResultsImpl в результаты.в Command :: prepareResults () мне нужен доступ к ResultsImpl.

M.

Ответы [ 4 ]

7 голосов
/ 24 июня 2011

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

class Object
{
public:
   Object();
  ~Object();

  int GetImplementationDetail();

private:
  std::unique_ptr< ObjectImpl > _pimpl;
};

int Object::GetImplementationDetail()
{
  return pimpl->GetImplementationDetail();
}

Кроме того, класс должен отвечать за одну вещь, только за одну, и должен иметь минимальный минимум зависимостей от других классов; если вы думаете, что другие классы должны иметь доступ к pimpl вашего объекта, то ваш дизайн, скорее всего, имеет недостатки.

edit после обновления автора: хотя ваш пример все еще довольно расплывчатый (или, по крайней мере, я не могу сказать его полное намерение), вы, похоже, неправильно истолковали эту идиому и теперь пытаетесь применить ее к случай, когда это бесполезно. Как отмечали другие, «Р» означает частное. Ваш класс результатов не имеет частной реализации, поскольку все это общедоступно. Так что либо попробуйте использовать то, что я упомянул выше, и ничего не «вводить», либо просто избавьтесь от прыща все вместе и используйте только класс Result. Если интерфейс класса вашего Результата должен быть настолько маленьким, что он является ничем иным, как указателем на другой класс, то в этом случае он, похоже, не очень полезен.

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

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

Если вы действительно считаете свой дизайн правильным, ничто не мешает вам предоставить заголовок «source-file-private», например:

include/
   foo.h
src/
   foo.impl.h
   foo.c
   bar.c

, а затем #includefoo.impl.h как в foo.c, так и в bar.c

foo.c:
    #include "foo.impl.h"
    ...

bar.c:
    #include "foo.impl.h"
    ...

Но опять же: как правило,

Принцип инверсии зависимости :

A.Модули высокого уровня не должны зависеть от модулей низкого уровня.Оба должны зависеть от абстракций.

B.Абстракции не должны зависеть от деталей.Детали должны зависеть от абстракций.

Также обязательно проверьте SOLID и GRASP , особенно пункт о слабой связи.

0 голосов
/ 24 июня 2011

Нет необходимости квалифицировать друга таким образом.

Если вы просто используете friend class OtherObject, класс OtherObject может получить доступ к необходимым внутренним компонентам.

Лично мой Pimpl - это просто struct (набор данных), и я оставляю методы для работы с ним в исходном классе.

0 голосов
/ 24 июня 2011

Создание элемента данных _pimple public ничего не купит, если эти другие классы не увидят определение его типа ObjectImpl - это именно то, что Прыщ намеревался предотвратить .

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

Конечно, все распространенные заявления об отказе от friend являются инструментом, который следует использовать как можно реже, насколько это возможно.

...