Разыменование общего указателя и вызов по ссылке - PullRequest
0 голосов
/ 06 мая 2020

Я использую некоторую вилку QMapControl и обнаружил ошибку. GeometryPointImage имеет геттер для изображения const QPixmap& image() const:

const QPixmap& GeometryPointImage::image() const
{
    // Is the image pixmap currently null?
    if (m_image == nullptr) { //std::shared_ptr<QPixmap> m_image;
        // Have we already constructed the null image pixmap?
        if (m_image_null == nullptr) { //std::unique_ptr<QPixmap> m_image_null;
            // Construct the null image pixmap.
            m_image_null.reset(new QPixmap);
        }
        // Return the null image pixmap.
        return *(m_image_null.get());
    } else {
        // Return the image pixmap.
        return *(m_image.get());
    }
}

Существуют различные сеттеры для m_image, а image() геттер используется в функции draw():

painter.drawPixmap(-pixmap_rect_px.rawRect().width() / 2.0, -pixmap_rect_px.rawRect().height() / 2.0, image()); // void drawPixmap(int x, int y, const QPixmap &pm)

Я могу уловить такое поведение: draw() вызовы функций image(), которые разыменовывают общий указатель, он переходит в drawPixmap и некоторые другие вызовы событий setImage(), где m_image присваивает новое значение и деструктор из общего указателя разрушает объект QPixmap, на который ссылается drawPixmap(), а затем приложение переходит в SIGSEGV.

Я думаю, что геттер, который возвращает ссылку на что-то, принадлежащее общему указателю, не является такой хорошей практикой, но какое решение является наиболее подходящим? Я не хочу копировать объект QPixMap или добавлять мьютекс в геттеры, сеттеры и draw(). Есть ли способ продлить жизнь указанного объекта (возможно, с чем-то похожим на qAsConst)? Должен ли геттер возвращать std::shared_ptr<QPixmap>?

UPD: Подробно: setImage() вызывается из основного потока, и ожидается, что этот сеттер излучает сигнал для перерисовки объекта. Но основной класс QMapControl также использует QtConcurrent::run() для перерисовки всей сцены и касается растрового изображения из какого-то другого потока. И поток №1 удаляет объект, когда поток, например, №6 (или №7) выполняет drawPixmap().

1 Ответ

0 голосов
/ 21 мая 2020

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

std::shared_ptr<QPixmap> GeometryPointImage::image() const
{
    // Is the image pixmap currently null?
    if (m_image == nullptr)
    {
        // Have we already constructed the null image pixmap?
        if (m_image_null == nullptr)
        {
            // Construct the null image pixmap.
            m_image_null.reset(new QPixmap);
        }

        // Return the null image pixmap.
        return std::make_shared<QPixmap>(*m_image_null.get());
    }
    else
    {
        // Return the image pixmap.
        return m_image;
    }
}

....

painter.drawPixmap(-pixmap_rect_px.rawRect().width() / 2.0, -pixmap_rect_px.rawRect().height() / 2.0, *image());

Сейчас , возвращение shared_ptr продлевает время жизни QPixMap, даже если оно было сброшено чем-то другим. Более того, метод image() использовался только в классе и нигде извне, поэтому эту проблему было легко исправить.

...