2016-12-07 6 views
0

Я пытаюсь передать объекты по значению между потоками с очередью.передать объект по значению между потоками

Поскольку существует несколько разных объектов, я создал абстрактный класс.

class message 
{ 
public : 
     virtual ~message(){}; 
}; 

то у меня есть подкласс для каждого типа сообщения

class a_specific_message : public message 
{ 
... 
}; 

Я прочитал this tutorial для реализации очереди, и я называю это следующим образом:

concurrent_queue<message> queue; 
a_specific_message m{1, 2, 3}; 
queue.push(m); 

Моя проблема что мне нужно переопределить operator=, чтобы очередь могла клонировать сообщение

popped_value=the_queue.front(); 

Я попытался добавить виртуального оператора, но он не вызывается в подклассе.

Я не знаю, как я мог достичь чего-то подобного, не передавая объект по ссылке.

+1

'concurrent_queue ' держит ** объекты ** типа 'сообщение'. Когда вы вставляете объект производного типа в очередь, объект получает разрез, и все, что было сохранено, является частью его сообщения. Невозможно вернуть исходный объект, как только это произойдет. Вам нужна очередь, содержащая ссылки или указатели. –

+0

@PeteBecker Я испугался, что это произойдет. Как я могу убедиться, что другой поток удалит объект, когда он будет сделан? – Marc

+2

Звучит как работа для 'shared_ptr' (которая является потокобезопасной) – Donnie

ответ

2

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


Однако, если полиморфизм, что вы хотите, позвольте мне предложить std::unique_ptr. Вы можете иметь здесь вам очередь указателей, объявленные как то: concurrent_queue<std::unique_ptr<message>>

Как вы сказали в комментариях:

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

Тогда я думаю, что std::unqiue_ptr подходит именно вам.

Если я перевожу свой код, он будет выглядеть так:

concurrent_queue<std::unique_ptr<message>> queue; 
auto m = std::make_unique<a_specific_message>(1, 2, 3); 

queue.push(std::move(m)); 

или просто:

concurrent_queue<std::unique_ptr<message>> queue; 

queue.push(std::make_unique<a_specific_message>(1, 2, 3)); 
// Or 
queue.push(new a_specific_message{1, 2, 3}); 

Затем совать значения:

auto popped_value = std::move(the_queue.front()); 

Тогда все будет удалена автоматически, не забудьте удалить любой указатель. std::unique_ptr был создан для этой цели.

Чтобы избежать явного двигаться в вашем коде, вы могли бы иметь что-то вроде pop_value в очереди, реализованная так:

T pop_value() { 
    auto value = std::move(the_queue.front()); 

    the_queue.pop(); 

    // use nrvo 
    return value; 
} 

Так вот, в ваших потоков, вы можете безопасно сделать:

{ 
    auto some_message = queue.pop_value(); 

    // stuff 

} // some_message deleted here. 
+0

Действительно ли make_unique C++ 14? Я получаю сообщение о том, что он не является членом std, и я использую C++ 11 – Marc

+1

Да, 'std :: make_unique' является частью C++ 14. Однако вы можете заменить std :: make_unique на простой старый 'new' –

+0

@Marc, чтобы увидеть мое редактирование. Я добавил пример с новым, который совместим с 'std :: unique_ptr' –

2

Как @PeteBecker пишет в комментариях, concurrent_queue<message> имеет message по значению. Когда экземпляр производного объекта помещается в очередь, он делает копию только message. Нарезка происходит.

Один из способов сделать объекты с удержанием в очереди нескольких типов, не прибегая к указателям, - использовать дискриминационный союз, например. boost::variant:

using message = boost::variant<specific_message_a, specific_message_b>; 

Нет общего базового класса для сообщений требуется здесь.

Недостатком этого подхода является то, что sizeof(message) является sizeof самого большого типа в том, что boost::variant<> список аргументов шаблона.

Смежные вопросы