Безопасно содержать произвольные данные в умном указателе - PullRequest
1 голос
/ 01 апреля 2011

Прежде всего: я искал половину Интернета, чтобы найти ответ с этим как наиболее подходящее решение.Это, однако, слишком тяжеловесно для меня, хотя, поэтому я выгляжу немного менее сложным.

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

. Класс ответа в настоящее время определяется следующим образом:

class COutboundMsg  
{  
public:  
    enum eTypeHint {  
        thNone,  
        thSomeType,  
        thLast  
    };  

    eTypeHint m_TypeHint;

    void* m_OutboundData;

    COutboundMsg(COutboundMsg::eTypeHint TypeHint, void* data);
};

COutboundMsg::COutboundMsg(COutboundMsg::eTypeHint TypeHint, void* data) :
m_TypeHint(TypeHint),
m_OutboundData(data)
{
}

Теперь, текущий способработа потребовала бы от пользователя сделать что-то вроде этого:

CSomeType* data = new CSomeType();  
COutboundMsg(COutboundMsg::thSomeType , (void*) data);

Пользователь на другом конце мог бы привести void* обратно к CSomeType* (используя подсказку типа) иудали это.

Это не нравится.

Я бы предпочел, чтобы m_OutboundData содержался в auto_ptr или что-то в этом роде, и позаботился бы о том, чтобы оно само удалилось.

Есть идеи?Может быть, совсем другой подход

Ответы [ 3 ]

3 голосов
/ 01 апреля 2011

Обычно полиморфизм используется для такой системы; любой элемент, который может быть помещен в очередь исходящих сообщений, происходит от QueueItem, и очередь содержит QueueItem* или интеллектуальный указатель на QueueItem.

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

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

2 голосов
/ 01 апреля 2011

shared_ptr хранит средство удаления, используемое для удаления объекта, и поэтому не зависит от статического типа указателя во время удаления. Так что-то вроде этого должно делать правильно:

COutboundMsg(eTypeHint TypeHint, shared_ptr<void> msg);

shared_ptr<CSomeType> data(new CSomeType);
COutboundMsg(COutboundMsg::thSomeType, data);
1 голос
/ 01 апреля 2011

Если набор сообщений известен заранее (передача / получение), а не полиморфизм, рассмотрим вариантный тип, я беру конкретно о boost::variant<>.

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

скажем, у меня есть два сообщения

struct Logon{}; 
struct Logout{};

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

typedef boost::variant<Logon, Logout> message_type;

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

struct process : boost::static_visitor<>
{
  void operator()(Logon const& cLogon)
  {
    // do stuff
  }
  void operator()(Logout const& cLogout)
  {
    // do stuff
  }
};

// method that processes a given message
void processMessage(message_type const& cMsg)
{
  // don't know what this message is, that's fine, hand it to the visitor
  boost::apply_visitor(process(), cMsg); // the correct overload will be called for the message
}

... Я думаю, что это немного лучше, чем void*;)

...