Шаблоны композиции в C ++, есть ли предпочтительный способ? - PullRequest
1 голос
/ 21 января 2011

Хорошо, позвольте мне начать с того, что это, вероятно, очень субъективно и аргументировано, и, вероятно, здесь нет места (не стесняйтесь закрываться, если это чувство взаимно).Сказав это, я смотрю на некоторый код и хочу прийти к стандартному подходу к композиции, поскольку кажется, что у разных людей разные стили - так что вот стили, которые я видел до сих пор (может быть больше..) Конкретная проблема компоновки, которую я рассматриваю, заключается в том, что класс B владеет экземпляром класса A, но A должен знать этот экземпляр, чтобы он мог вызывать методы B.

#include <iostream>

using namespace std;

namespace forward
{
  class B;

  class A
  {
  public:
    A(B& b); 
  private:
    B& inst;
  };

  class B
  {
  public:
    B() : inst(*this) {}

    void foo() { cout << "forward::B::foo()" << endl; }

  private:
    A inst;
  };

  A::A(B& b) : inst(b) { inst.foo(); }
}

namespace interface
{
  struct IB
  {
    virtual void foo() = 0;
  };

  class A
  {
  public:
    A(IB* b) : inst(b) { inst->foo(); }
  private:
    IB* inst;
  };

  class B : public IB
  {
  public:
    B() : inst(this) {}

    virtual void foo() { cout << "interface::B::foo()" << endl; }

  private:
    A inst;
  };
}

namespace templated
{
  template <typename IB>
  class A
  {
  public:
    A(IB& b) : inst(b) { inst.foo(); }
  private:
    IB& inst;
  };

  class B
  {
  public:
    B() : inst(*this) {}

    void foo() { cout << "templated::B::foo()" << endl; }

  private:
    A<B> inst;
  };   
}

int main(void)
{
  forward::B b1;    
  interface::B b2;    
  templated::B b3;    

  return 0;
}

Из этого я вижу следующее (не полное):

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

интерфейсы Дополнительный багаж (конструкции базового класса, вызовы виртуальных функций и т. Д.)

templated Я не вижу никаких проблем с этим, кроме компиляциипроблемы (т.е. нелепые сообщения об ошибках и т. д.)

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


РЕДАКТИРОВАТЬ: я думаю, что тривиальный пример не помог, вв данном конкретном случае B является менеджером ресурсов и владеет различными компонентами, которые связаны между собой (A) - например, различными сетевыми соединениями и т. д. Все подкомпоненты могут обращаться друг к другу через B - вся система раньше была кучей синглетонов... Таким образом, единственная причина, по которой A знает о B, заключается в том, что он обеспечивает доступ к другому компоненту, который необходим A ...

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

Ответы [ 6 ]

3 голосов
/ 21 января 2011

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

1 голос
/ 21 января 2011

Я бы выбрал первое решение, оно делает именно то, что вы хотите.

Я бы рассмотрел два других решения Обходные пути , поскольку:

  • интерфейсы здесь не нужны, и, как вы сказали, это добавляет сложности и накладные расходы.

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


Существует даже четвертый способ облегчить эту проблему: ослабление объявления перед использованием правило

0 голосов
/ 21 января 2011

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

Я бы рассмотрел пересылки объявлений , если принадлежащий класс(или может быть функционально) вложенный класс.Что еще будет вызывать А, то B?

interface будет использоваться, если разумно предположить, что используются разные обратные вызовы, но только 1 на класс (менее вероятно).

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

Честно говоря, я не вижу, что шаблонирует покупает вас, возможно, потому что я еще не использовал его.

0 голосов
/ 21 января 2011

Подобные циклические зависимости часто могут указывать на необходимость пересмотра проекта. Часто лучшим решением является введение третьего класса C, который опосредует / взаимодействует между A и B (или с которыми A и B взаимодействуют).

Если, однако, A и B действительно чрезвычайно тесно связаны, используйте предварительные объявления, как в первом примере, и поместите оба класса в один заголовок.

РЕДАКТИРОВАТЬ (для редактирования ОП): В этом случае это звучит так, как будто вам нужен класс «алгоритма», который знает о менеджере и всех подобъектах и ​​делегирует каждый фрагмент работы соответствующему содержанному объекту (получая его из контейнера по мере необходимости). Например, он может передать ссылку на B в методы A, которые в ней нуждаются, вместо введения циклической зависимости.

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

0 голосов
/ 21 января 2011

Это не композиция, а просто наследование, то есть B эффективно наследуется от A, и мне просто кажется, что вам нужно перепроектировать свои классы.

Я лично использую решение для переадресации, но, конечно, в моих классах B не содержит A.

0 голосов
/ 21 января 2011

Если A и B объявлены в одном заголовочном файле, «прямое» решение является наиболее простым IMO.В противном случае используйте решение «interface».

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