Подтвердить что ты не робот

Какая лучшая стратегия для типизации общих указателей?

У меня есть быстрый вопрос относительно использования typedef для длинных шаблонов. Суть: я нашел себя в чем-то рассол - там, похоже, не место для размещения typedefs, кроме локальных для клиентских функций. Хотя есть похожие вопросы SO (см. здесь), ничто, кажется, не обращается к этому точно. Обратите внимание, что в этом вопросе не рассматривается вопрос о том, желательны ли typedefs в дальнейшем: я попытался упростить вещи для пояснительных целей.

Моя проблема возникла при работе с boost::shared_ptr<T>. В принципе, я хочу сделать следующее:

#include <boost/shared_ptr.hpp>
typedef boost::shared_ptr<Widget> WidgetPtr;

Размещение этого typedef в заголовке объявления Widget кажется уродливым. Здесь, по-видимому, есть два соображения: (i) если Widget сам не использует общие указатели в своих членах, мы добавили дополнительный include (поскольку мы не можем переслать объявление boost::shared_ptr template class- исправьте меня, если я ошибаюсь?) (ii) если мы хотим использовать этот typedef во время объявления другого класса (вызов этого класса Foo), мы нарушаем лучшие практики, включая Widget.h вместо простого переадресации объявления Widget или включая WidgetFwd.h... если этот typedef не дублируется в последнем. Кроме того, во время объявления самой Widget не имеет смысла вводить typedef boost::shared_ptr<Widget> - мы, кажется, смешиваем объявление Widget с ожиданием того, как клиенты будут использовать интерфейс Widget.

Хорошо, так плохо, но это хуже: если я не пытаюсь выполнить какую-либо комбинацию выше, я получаю дубликат typedefs в клиентском коде, что приводит к несогласованности (и, следовательно, скорее всего, к ошибке) - весь смысл что при задании Widget a WidgetPtr typedef должен действовать как тип в своем собственном праве. Пример: мы не хотим, чтобы Foo использовал один WidgetPtr, typedef boost::shared_ptr, а Bar использует WidgetPtr как typedef для std::auto_ptr.

Другой метод (и один из немногих, который я видел в онлайн-обсуждении) должен был сделать typedef публичным членом Widget, а затем использовать Widget::Ptr:

class Widget {
// ...
public:
     typedef boost::shared_ptr<Widget> Ptr;
};

Опять же, мне это не нравится, поскольку (i) он предполагает, что тип указателя как-то является членом класса, и (ii) он приводит к неустойчивому интерфейсу. Хуже того: поскольку каждый класс, который я пишу, потенциально можно указать на использование интеллектуальных указателей, я в конечном итоге преследую воображаемый клиентский хвост. Уродливый, уродливый, уродливый.

Как бы то ни было, я удалил typedef из этой кодовой базы (поскольку они привели к серьезной путанице, дублированию) и повторно представили их локально в выбранных функциях. Здесь снова возникает проблема с непоследовательным использованием, но это не так уж тяжело.

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

Я пропустил что-то очевидное или это просто сложно?

PS - Извинения за длину выше; Я не мог найти более простой способ полностью выразить проблему.

4b9b3361

Ответ 1

Мне не нравится библиотека, диктующая использование определенного умного указателя, но я терплю это, если это необходимо.

Если вы хотите заставить пользователей всегда использовать shared_ptr для управления виджетами, это невозможно, поэтому даже не пытайтесь попробовать.

С другой стороны, если у вас есть метод из Widget, который возвращает boost::shared_ptr<Widget>, то предоставление (sane) typedef может упростить код клиента.

Поэтому я бы предложил использовать внутренний typedef:

class Widget
{
public:
  typedef boost::shared_ptr<Widget> Ptr;

  Ptr AccessFirstChild();
}; // class Widget

и в этом случае он идеально подходит для #include необходимых заголовков.

Ответ 2

Кроме того, нет смысла вводить typedef boost:: shared_ptr во время объявления самого виджета - мы, кажется, смешиваем объявление Widget с ожиданием того, как клиенты будут использовать интерфейс Widget.

Прежде всего, это совсем не так - в конце концов, способы использования (и использования) клиентов интерфейсом являются частью самого интерфейса; и для С++, не будучи собранным мусором, управление памятью объектов является довольно важной частью их интерфейса.

Итак, есть два случая. В одном случае Widget ожидал, что он будет использоваться с помощью общего указателя. Это будет означать, что, например. дочерние виджеты, полученные из виджета, возвращаются как shared_ptr s, каждый созданный виджет имеет shared_ptr и т.д. Было бы полностью законным typedef WidgetPtr в том же заголовке, что и Widget.

Во втором случае Widget будет ожидать управления, например. обычными new и delete. Клиенты могут использовать shared_ptr в особых случаях, но ничего не говорит, например. в противном случае процедура диалога с принтером не может использовать auto_ptr. Клиенты должны быть готовы к тому, что если wptr является shared_ptr, строка

shared_ptr<Widget> w2(wptr->firstChild()->parent());

приводит к катастрофе.

Ваш вопрос, кажется, указывает, что это ваше дело. Итак, ИМХО, что вы сделали, все в порядке. Клиенты могут выбирать средства управления объектами Widget, если это не влияет на других клиентов.

Ответ 3

Вы, по моему мнению, слишком задумываетесь об этом. Все, кто хочет иметь shared_ptr<Widget>, все равно должны будут включать в себя заголовочный файл Widget. Ввод typedef (что является хорошей идеей imo) в Widget.h, составляет 100% смысла для меня.

Ответ 4

Мой подход (использование типов подстроек, только потому, что я это делаю)

class Type
{
    public:
        typedef shared_ptr<Type>        ptr;
        typedef shared_ptr<const Type>  const_ptr;
};

Я нашел версию const_ptr довольно полезной.

Ответ 5

Я использовал для структурирования своего кода на С++ в библиотеках. Библиотека будет иметь кучу заголовков для потребления клиентов, все внутри каталога include/LibraryName. Кроме того, у меня будет один заголовок с именем Fwd.h внутри этой директории с простыми объявлениями всех классов вместе с их указателями typedefs.

Кроме того, каждый публичный заголовок будет включать Fwd.h, так что включение заголовка автоматически предоставит вам все объявления вперед и указатели typedef. На практике это было очень хорошо.

Однако не все классы необходимо разместить в shared_ptr. Я бы только создал typedefs указателей для типов, которые, как я ожидал, будут созданы динамически, и в этом случае я бы предоставил factory. Это дает дополнительное преимущество, которое может уйти с предоставлением клиентского кода только с типами интерфейсов и скрыть бетонные реализации в вашей библиотеке src. Не конкретно то, о чем вы просили, но это дает полную картину моего метода. В качестве конечной точки также можно предоставить заголовок удобства под названием LibraryName.h, который включает в себя Fwd.h и все другие публичные заголовки.

Удачи!

Ответ 6

Обычно я использую этот подход для облегчения ввода и создания общего интерфейса общего указателя для классов. Обратите внимание на С++ 0x.

#include <iostream>
#include <memory>

template <class T>
struct SharedVirtual
{
   typedef std::shared_ptr<T> VPtr;
};

template <class T>
struct Shared
{
   typedef std::shared_ptr<T> Ptr;

   template <class... P>
   static Ptr ptr(P&&... args) 
   { 
      return std::make_shared<T>(std::forward<P>(args)...); 
   }
};

class Foo : public SharedVirtual<Foo>
{
   public:
      virtual void foo() const = 0;
};

class Test : public Foo, public Shared<Test>
{
   public:
      void foo() const { std::cout << "Hai u!" << std::endl; }
};

void print(const Foo::VPtr& ptr)
{
   ptr->foo();
}

int main()
{
   auto ptr = Test::ptr();
   print(ptr);
}

Ответ 7

Вторая часть первая: используйте пространство имен, то есть:

namespace WidgetStuff {
  class Widget { ..
  typedef shared_ptr<Widget> WidgetPtr;
  ..

Если вы хотите разбить его:

namespace WidgetStuff {
   class Widget { ...
}
...
namespace WidgetStuff { 
  typedef ...

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

И теперь на часть 1 тоже ответят, если вы выберете, вы можете сделать:

#include <widget.h>
#include <widget_utils.h>

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