Идиома Pimpl с наследованием

Это решение кода после того, как результаты получены из Mongo. Использование карты для хранения индекса и затем замены значений.

catDetails := make([]CategoryDetail, 0)
err = sess.DB(mdb).C("category").
    Find(bson.M{
    "_id":       bson.M{"$in": path},
    "is_active": 1,
    "name":      bson.M{"$ne": ""},
    "url.path":  bson.M{"$exists": true, "$ne": ""},
}).
    Select(
    bson.M{
        "is_active": 1,
        "name":      1,
        "url.path":  1,
    }).All(&catDetails)

if err != nil{
    return 
}
categoryOrderMap := make(map[int]int)

for index, v := range catDetails {
    categoryOrderMap[v.Id] = index
}

counter := 0
for i := 0; counter < len(categoryOrderMap); i++ {
    if catId := int(path[i].(float64)); catId > 0 {
        fmt.Println("cat", catId)
        if swapIndex, exists := categoryOrderMap[catId]; exists {
            if counter != swapIndex {
                catDetails[swapIndex], catDetails[counter] = catDetails[counter], catDetails[swapIndex]
                categoryOrderMap[catId] = counter
                categoryOrderMap[catDetails[swapIndex].Id] = swapIndex
            }
            counter++
        }
    }
}
16
задан Igor Oks 29 January 2009 в 12:00
поделиться

5 ответов

class A
{
    public:
      A(bool DoNew = true){
        if(DoNew)
          pAImpl = new AImpl;
      };
      void foo(){pAImpl->foo();};
    protected:
      void SetpAImpl(AImpl* pImpl) {pAImpl = pImpl;};
    private:
      AImpl* pAImpl;  
};
class AImpl
{
    public:
      void foo(){/*do something*/};
};

class B : public A
{
    public:
      B() : A(false){
          pBImpl = new BImpl;
          SetpAImpl(pBImpl);
      };
      void bar(){pBImpl->bar();};    
    private:
      BImpl* pBImpl;  
};        

class BImpl : public AImpl
{
    public:
      void bar(){/*do something else*/};
};
7
ответ дан 30 November 2019 в 23:24
поделиться

Как stefan.ciobaca сказал, если бы Вы действительно хотели быть растяжимыми, то Вы хотели бы pAImpl быть защищенными.

Однако Ваше определение в B из void bar(){pAImpl->bar();}; кажется нечетным, поскольку bar метод на BImpl а не AImpl.

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

  1. Ваша альтернатива (3).
  2. вариация А на (3), в котором BImpl расширяется AImpl (наследование существующей реализации foo вместо того, чтобы определить другого), BImpl, определяет bar, и B использование его частное BImpl* pBImpl для доступа к обоим.
  3. Делегация, в которой B содержит частные указатели на каждый из [1 114] и BImpl и вперед каждый из [1 116] и bar соответствующему реализатору.
1
ответ дан 30 November 2019 в 23:24
поделиться

Я думаю, что лучший способ с чисто объектно-ориентировано-теоретической точки зрения не состоит в том, чтобы заставить BImpl наследоваться AImpl (это, что Вы имели в виду в опции 3?). Однако наличие, которое BImpl получают из AImpl (и передача желаемого impl конструктору A) в порядке также, при условии, что pimpl членская переменная const. Действительно не имеет значения, используете ли Вы получить функции или непосредственно получаете доступ к переменной от производных классов, если Вы не хотите осуществить правильность константы на производных классах. Разрешение производным классам изменить pimpl не является хорошей идеей - они могли разрушить всю инициализацию A - и, и при этом разрешение не является изменением базового класса это хорошая идея. Рассмотрите это расширение своего примера:

class A
{
protected:
   struct AImpl {void foo(); /*...*/};
   A(AImpl * impl): pimpl(impl) {}
   AImpl * GetImpl() { return pimpl; }
   const AImpl * GetImpl() const { return pimpl; }
private:
   AImpl * pimpl;
public:
   void foo() {pImpl->foo();}


   friend void swap(A&, A&);
};

void swap(A & a1, A & a2)
{
   using std::swap;
   swap(a1.pimpl, a2.pimpl);
}

class B: public A
{
protected:
   struct BImpl: public AImpl {void bar();};
public:
   void bar(){static_cast<BImpl *>(GetImpl())->bar();}
   B(): A(new BImpl()) {}

};

class C: public A
{
protected:
   struct CImpl: public AImpl {void baz();};
public:
   void baz(){static_cast<CImpl *>(GetImpl())->baz();}
   C(): A(new CImpl()) {}
};

int main()
{
   B b;
   C c;
   swap(b, c); //calls swap(A&, A&)
   //This is now a bad situation - B.pimpl is a CImpl *, and C.pimpl is a BImpl *!
   //Consider:
   b.bar(); 
   //If BImpl and CImpl weren't derived from AImpl, then this wouldn't happen.
   //You could have b's BImpl being out of sync with its AImpl, though.
}

, Хотя у Вас не могло бы быть подкачки () функция, можно легко забеременеть подобного появления задач, особенно если A является присваиваемым, или случайно или намерение. Это - несколько тонкое нарушение принципа замещаемости Liskov. Решения к также:

  1. не изменяют pimpl участников после конструкции. Объявите, что они AImpl * const pimpl. Затем конструкторы порожденного класса могут передать соответствующий тип, и остальная часть производного класса может удрученный уверенно. Однако затем Вы не можете, например, делать подкачки неброска, присвоения или копию на записи, потому что эти методы требуют, чтобы можно было изменить pimpl участника. Однако однако Вы, вероятно, действительно не намереваетесь сделать эти вещи, если у Вас есть иерархия наследования.

  2. Имеют не связанный (и немой) AImpl и классы BImpl для частных переменных A и B, соответственно. Если B хочет сделать что-то к A, то используйте общественность A или защищенный интерфейс. Это также сохраняет наиболее распространенную причину для использования pimpl: способность скрыть определение AImpl в cpp файле, который не могут использовать производные классы, таким образом, половина Вашей программы не должна перекомпилировать, когда реализация A изменяется.

2
ответ дан 30 November 2019 в 23:24
поделиться

Я сделал бы (1), потому что рядовые A или никакой бизнес для B.

На самом деле я не передал бы его, как Вы предполагаете, потому что A делает свое собственное в A:: (). Вызов pApimpl->whatever() от Еще раз также не соответствующий (частный средства частный).

0
ответ дан 30 November 2019 в 23:24
поделиться

Корректный путь состоит в том, чтобы сделать (2).

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

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

РЕДАКТИРОВАНИЕ

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

, Если Вы должны сделать, чтобы Bimpl наследовал Aimpl и Вы хотите выставить последовательный интерфейс им через A, и B, B должен был бы также наследовать A.

Одна вещь можно сделать для упрощения вещей в этом сценарии, должен иметь B, наследовались A и только изменяют конструктора, таким образом что B:: B (...) {} создает Bimpl, и добавьте отправки для всех методов Bimpl, которые не находятся в Aimpl.

-3
ответ дан 30 November 2019 в 23:24
поделиться
Другие вопросы по тегам:

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