C++: как правильно передать переменную, возвращаемую deque::front(), из функции?

Я работаю над многопоточной программой, в которой "std::deque‹ MyObject > myBuffer" используется в качестве буфера FIFO, поток-производитель постоянно добавляет пользовательские объекты в конец очереди с помощью push_back(), а поток-потребитель использует вспомогательная функция для извлечения объекта и обработки синхронизации и мьютекса.

std::deque< MyObject > myBuffer;
std::mutex mtx;

int main() {
    std::thread producerThread(producer());
    std::thread consumerThread(consumer());

    // other code

    return 0;
}

Функция продюсера:

void producer() {
    while (somecondition) {

        // code producing MyObject object

        std::lock_guard<std::mutex> lck(mtx);
        myBuffer.push_back(object);
    }
}

Потребительская функция:

void consumer() {
    while(somecondition) {
        MyObject object1, object2;
        if (retrieve(object1)) {
            // process object1
        }
        if (retrieve(object2)) {
            // process object2
        }
    }
}

Моя текущая вспомогательная функция выглядит так:

bool retrieve(MyObject & object) {
    // other code ...
    std::lock_guard<std::mutex> lck(mtx);
    if (!myBuffer.empty()) {
        object = myBuffer.front();
        myBuffer.pop_front();
        return true;
    } else {
        return false;
    }
}

Однако я быстро понял, что deque::front() возвращает ссылку на первый элемент в контейнере. А «объект» — это MyObject&, поэтому, насколько я понимаю, объекту передается только ссылка на первый элемент в двухсторонней очереди, и в результате, когда я вызываю pop_front(), указанный элемент должен исчезнуть, а объектная переменная содержит недействительную ссылку. Удивительно, но когда я действительно запускал код, все работало не так, как я ожидал. Так может ли кто-нибудь помочь мне понять, как работает этот «deque::front() возвращает ссылку»? Спасибо.


person xk103247    schedule 14.09.2018    source источник
comment
Я думаю, это поможет вам. stackoverflow.com/ вопросы/11554083/   -  person Jie Wei    schedule 14.09.2018


Ответы (3)


Он работает правильно, и это ожидаемое поведение.

Вы не назначаете ссылку - вы не можете, ссылки C++ неизменяемы. Вы фактически копируете значение. Вот как это должно работать. Семантика присваивания foo = ..., когда foo является ссылкой, примерно такова: «скопировать правое значение в место, на которое ссылается foo».

При наличии ссылки с правой стороны копируется указанное значение.

В вашем случае строка object = myBuffer.front(); копирует переднее значение deque в переменную object1 или object2 в consumer() соответственно вызову. Последующий вызов .pop_front() уничтожает значение в двухсторонней очереди, но не влияет на уже скопированное значение.

person Frax    schedule 15.09.2018

я не могу понять вашу цель, может быть, вы можете попробовать deque::at()

person Wjiyao    schedule 14.09.2018
comment
Цель функции retrieve() состоит в том, чтобы получить значение из очереди для обработки вызывающим потоком. Он содержит все операции deque и lock, поэтому его можно вызывать в нескольких местах моего кода, и вызывающей функции не нужно беспокоиться об управлении deque и sync. Мне нужно выполнить deque::pop_front(), потому что я не хочу, чтобы очередь росла бесконечно во время выполнения моей программы. Таким образом, использование deque::at() не может достичь моей цели. - person xk103247; 14.09.2018

pop_front() удаляет первый элемент из очереди. Он не удаляет объект. Таким образом, ссылка на объект после вызова pop_front() должна работать.

Обновлять -

#include <iostream>
#include <queue>
#include <algorithm>

class newClass {
public:
    newClass () {
    }

    ~newClass () {
        std::cout << " Destructor is called. " << "\n";
    }

    newClass(const newClass &obj) {
        std::cout << "Copy is called." << "\n";
    }

    void print(void) {
        std::cout << "Hi there !" << "\n";
    }
};

void queueWithPointer(void) {
    std::deque<newClass *> deque;
    deque.push_back(new newClass());
    deque.push_front(new newClass());
    newClass *b = deque.front();
    std::cout << "pop_front starts" << "\n";
    deque.pop_front();
    std::cout << "pop_front ends" << "\n";
    b->print();
}

void queueWithObjects(void) {
    std::deque<newClass> deque;
    deque.push_back(newClass());
    deque.push_front(newClass());
    newClass ba = deque.front();
    std::cout << "pop_front starts" << "\n";
    deque.pop_front();
    std::cout << "pop_front ends" << "\n";
    ba.print();
}

int main()
{
    queueWithPointer();
//  queueWithObjects();

    return 0;
}

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

person Ajay Srivastava    schedule 14.09.2018
comment
Согласно ссылке, pop_front() не только удаляет первый элемент из контейнер, но и уничтожает его. - person xk103247; 14.09.2018
comment
Обновлен ответ, чтобы объяснить это. - person Ajay Srivastava; 15.09.2018