Russian Qt Forum
Сентябрь 23, 2024, 22:29 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 [2] 3   Вниз
  Печать  
Автор Тема: [Решено] Использование std::shared_ptr и std::unique_ptr c Qt объектами  (Прочитано 24183 раз)
8Observer8
Гость
« Ответ #15 : Июнь 18, 2014, 16:34 »

Что значит нельзя инициализировать в секции приват?
А ведь и вправду можно:
Код
C++ (Qt)
std::shared_ptr<int> foo = std::make_shared<int> (10);
 
Но в моём случае это не подходит. У меня сначала создаётся переменная: std::shared_ptr<QNetworkAccessManager> manager. А потом m_reply.

Смотри мой предыдущий ответ.
Я как раз под него код переписывал и пробовал. Вот так и буду делать. Объявлять переменную:
Код
C++ (Qt)
private:
   std::shared_ptr<QNetworkReply> m_reply;
 

А потом инициализировать:
Код
C++ (Qt)
   std::shared_ptr<QNetworkAccessManager> manager =
           std::make_shared<QNetworkAccessManager>( new QNetworkAccessManager( this ) );
 
   m_reply.reset(manager->get( QNetworkRequest( QUrl( url ) ) ) );
 

Спасибо всем огромное!

Я ещё нашёл, что так нельзя писать:
Код
C++ (Qt)
std::shared_ptr<QNetworkAccessManager>
               manager( new QNetworkAccessManager( this ) );
 

Так как утечки памяти возможны в случае исключений. И вроде два раза место выделяется. Я тонкости ещё не знаю... Вот так правильно:
Код
C++ (Qt)
       std::shared_ptr<QNetworkAccessManager> manager =
               std::make_shared<QNetworkAccessManager>( new QNetworkAccessManager( this ) );
 

Вот так исправил:
Код
C++ (Qt)
#ifndef DOWNLOADER_H
#define DOWNLOADER_H
 
#include <memory>
 
#include <QObject>
#include <QString>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QNetWorkAccessManager>
 
class Downloader : public QObject
{
   Q_OBJECT
 
   Downloader( )
   {
//        connect( m_reply, SIGNAL( finished( ) ),
//                 this, SLOT(replyFinished( ) ) );
   }
 
   void fetch( const QString &url )
   {
       std::shared_ptr<QNetworkAccessManager> manager =
               std::make_shared<QNetworkAccessManager>( new QNetworkAccessManager( this ) );
 
       m_reply.reset(manager->get( QNetworkRequest( QUrl( url ) ) ) );
   }
 
signals:
   void signalWithContent( QString * );
 
private slots:
   void replyFinished( )
   {
       QByteArray data = m_reply->readAll( );
       QString content( data );
       emit signalWithContent( &content );
   }
 
private:
   std::shared_ptr<QNetworkReply> m_reply;
};
 
#endif // DOWNLOADER_H
 
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #16 : Июнь 18, 2014, 16:43 »

А вас не смущает, что manager будет разрушаться при выходе из fetch с удалением всех созданных QNetworkReply?
Записан
8Observer8
Гость
« Ответ #17 : Июнь 18, 2014, 16:57 »

Я думал, что нам объект "manager" нужен только чтобы "m_reply" инициализировать. Разве нет? На всякий случай, перетащу объект "manager" в private:
Код
C++ (Qt)
#ifndef DOWNLOADER_H
#define DOWNLOADER_H
 
#include <memory>
 
#include <QObject>
#include <QString>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QNetWorkAccessManager>
 
class Downloader : public QObject
{
   Q_OBJECT
 
   Downloader( )
   {
//        connect( m_reply, SIGNAL( finished( ) ),
//                 this, SLOT(replyFinished( ) ) );
   }
 
   void fetch( const QString &url )
   {
       m_reply.reset(m_manager->get( QNetworkRequest( QUrl( url ) ) ) );
   }
 
signals:
   void signalWithContent( QString * );
 
private slots:
   void replyFinished( )
   {
       QByteArray data = m_reply->readAll( );
       QString content( data );
       emit signalWithContent( &content );
   }
 
private:
   std::shared_ptr<QNetworkAccessManager> m_manager =
           std::make_shared<QNetworkAccessManager>( new QNetworkAccessManager( this ) );
 
   std::shared_ptr<QNetworkReply> m_reply;
};
 
#endif // DOWNLOADER_H
 

У меня просто есть похожий код (правда он не совсем рабочий, но по другим причинам). Там тоже создаётся временный объект "manager":
Код
C++ (Qt)
#include "VKAuth.h"
#include <QNetworkRequest>
#include <QMessageBox>
 
VKAuth::VKAuth(QString app, QWidget* parent) : QWebView(parent)
{
 
   QObject::connect(this, SIGNAL(urlChanged(QUrl)),
                    SLOT(slotLinkChanged(QUrl))
                    );
   m_app = app;
   loadLoginPage();
}
 
void VKAuth::loadLoginPage(){
   QUrl url("https://oauth.vk.com/authorize");
 
   url.addQueryItem("client_id", m_app);
   url.addQueryItem("layout", "https://oauth.vk.com/blank.html");
   url.addQueryItem("display", "popup");
   url.addQueryItem("scope", "8");
   url.addQueryItem("response_type", "token");
 
   load(url);
}
 
void VKAuth::slotLinkChanged(QUrl url)
{
   if ("/blank.html" == url.path()) {
       QRegExp regexp("access_token=([^,]+)&expires_in=([^,]+)&user_id=([^,]+)");
 
       QString str= url.fragment();
 
       if( -1 != regexp.indexIn(str) ) {
           m_access_token = regexp.cap(1);
           m_expires_in = regexp.cap(2);
           m_user_id = regexp.cap(3);
           slotRequest();
       }
   }
   else if("/api/login_failure.html" == url.path()){
       emit unsuccess();
   }
}
 
void VKAuth::slotRequest() {
   QUrl request(QString("https://api.vk.com/method/%1.xml?").arg("friends.get"));
   request.addQueryItem("user_id", m_user_id);
   request.addQueryItem("fields", "online");
   request.addQueryItem("v", "5.2");
   request.addQueryItem("access_token", m_access_token);
 
   QNetworkAccessManager *manager = new QNetworkAccessManager(this);
   m_http = manager->get(QNetworkRequest(request));
   QObject::connect(m_http, SIGNAL(finished()), this, SLOT(slotDone()));
}
 
void VKAuth::slotDone() {
   QString russian = QString::fromUtf8(m_http->readAll());
   QDomDocument dom;
   QByteArray aByteArray = russian.toUtf8();
   if (!dom.setContent(aByteArray)) {
       QMessageBox::critical(this, tr("Error"), tr("Failed to parse the file into a DOM tree"));
       return;
   }
 
   emit success(dom);
   hide(); //hide this window
   m_http->close();
}
 
Записан
8Observer8
Гость
« Ответ #18 : Июнь 18, 2014, 17:24 »

Ещё одна проблема. Помогите, пожалуйста. Понятия не имею, что не так. Выдаётся сообщение:
Цитировать
DownLoader.h:19: error: no matching function for call to 'Downloader::connect(std::shared_ptr<QNetworkReply>&, const char*, Downloader* const, const char*)'
                  this, SLOT(replyFinished( ) ) );

Код
C++ (Qt)
#ifndef DOWNLOADER_H
#define DOWNLOADER_H
 
#include <memory>
 
#include <QObject>
#include <QString>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QNetWorkAccessManager>
 
class Downloader : public QObject
{
   Q_OBJECT
 
   Downloader( )
   {
       connect( m_reply, SIGNAL( finished( ) ),
                this, SLOT( replyFinished( ) ) );
   }
 
   void fetch( const QString &url )
   {
       m_reply.reset(m_manager->get( QNetworkRequest( QUrl( url ) ) ) );
   }
 
signals:
   void signalWithContent( QString * );
 
private slots:
   void replyFinished( )
   {
       QByteArray data = m_reply->readAll( );
       QString content( data );
       emit signalWithContent( &content );
   }
 
private:
   std::shared_ptr<QNetworkAccessManager> m_manager =
           std::make_shared<QNetworkAccessManager>( new QNetworkAccessManager( this ) );
 
   std::shared_ptr<QNetworkReply> m_reply;
};
 
#endif // DOWNLOADER_H
 
« Последнее редактирование: Июнь 18, 2014, 17:28 от 8Observer8 » Записан
kambala
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4744



Просмотр профиля WWW
« Ответ #19 : Июнь 18, 2014, 18:09 »

В исходниках я нашёл:
Код
C++ (Qt)
QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request)
{
   return d_func()->postProcess(createRequest(QNetworkAccessManager::GetOperation, request));
}
 

Но мне это не о чём не говорит...
можно еще и в метод createRequest() было заглянуть Улыбающийся
Я думал, что нам объект "manager" нужен только чтобы "m_reply" инициализировать. Разве нет?
разве нет. почитай про класс менеджера.
У меня просто есть похожий код (правда он не совсем рабочий, но по другим причинам). Там тоже создаётся временный объект "manager"
там обычный указатель, который не умирает по выходе из области видимости (но есть утечка памяти пока объект VKAuth будет жить)

а для чего тебе вообще сохранять этот QNetworkReply? у менеджера же есть сигнал, в который QNetworkReply передается.
Записан

Изучением C++ вымощена дорога в Qt.

UTF-8 has been around since 1993 and Unicode 2.0 since 1996; if you have created any 8-bit character content since 1996 in anything other than UTF-8, then I hate you. © Matt Gallagher
8Observer8
Гость
« Ответ #20 : Июнь 18, 2014, 18:40 »

kambala, Спасибо! Улыбающийся

Вот ответ на второй вопрос:
Код:
    Downloader( )
    {
        connect( m_reply.get(), SIGNAL( finished( ) ),
                 this, SLOT( replyFinished( ) ) );
    }

P.S. Правда я пока не понял, почему работает... Здесь подсказали: http://www.qtcentre.org/threads/59501-Using-std-shared_ptr-for-Qt-objects?p=264172#post264172
Записан
8Observer8
Гость
« Ответ #21 : Июнь 18, 2014, 18:58 »

P.S. Правда я пока не понял, почему работает...
А теперь понял. Переменная m_reply имеет тип std::shared_ptr<QNetworkReply>. Чтобы получить указатель на QNetworkReply надо вызвать метод get класса std::shared_ptr: http://www.cplusplus.com/reference/memory/shared_ptr/get/
Записан
8Observer8
Гость
« Ответ #22 : Июнь 18, 2014, 20:05 »

Приложение дописал, но как раз в том модуле ошибка. Невозможно соединить сигнал и слот. Почему-то m_reply.get( ) возвращает null, если я правильно понял.

Цитировать
QObject::connect: Cannot connect (null)::finished( ) to Downloader::replyFinished( )
QObject::connect: Cannot connect (null)::QNetworkReply::downloadProgress( qint64 bytesReceived, qint64 bytesTotal ) to Downloader::slotDownloadProgress(qint64 bytesReceived, qint64 bytesTotal )
QObject::connect: No such slot QProgressBar::replyProgress( qint64 bytesReceived, qint64 bytesTotal ) in D:/Qt/QtPortfolio/0009_ParserHtmlRGB/ParserHtmlRGB/Dialog.cpp:19
QObject::connect:  (receiver name: 'progressBar')

Код
C++ (Qt)
#ifndef DOWNLOADER_H
#define DOWNLOADER_H
 
#include <memory>
 
#include <QObject>
#include <QString>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QNetWorkAccessManager>
 
class Downloader : public QObject
{
   Q_OBJECT
 
public:
   Downloader( )
   {
       connect( m_reply.get( ), SIGNAL( finished( ) ),
                this, SLOT( replyFinished( ) ) );
       connect( m_reply.get( ), SIGNAL( QNetworkReply::downloadProgress( qint64 bytesReceived, qint64 bytesTotal ) ),
                this, SLOT( slotDownloadProgress(qint64 bytesReceived, qint64 bytesTotal ) ) );
   }
 
   void fetch( const QString &url )
   {
       m_reply.reset(m_manager->get( QNetworkRequest( QUrl( url ) ) ) );
   }
 
signals:
   void signalWithContent( QString * );
   void downloadProgress(qint64 bytesReceived, qint64 bytesTotal);
 
private slots:
   void replyFinished( )
   {
       QByteArray data = m_reply->readAll( );
       QString content( data );
       emit signalWithContent( &content );
   }
 
   void slotDownloadProgress( qint64 bytesReceived, qint64 bytesTotal )
   {
       emit downloadProgress( bytesReceived, bytesTotal );
   }
 
private:
   std::shared_ptr<QNetworkAccessManager> m_manager =
           std::make_shared<QNetworkAccessManager>( new QNetworkAccessManager( this ) );
 
   std::shared_ptr<QNetworkReply> m_reply;
};
 
#endif // DOWNLOADER_H
 
« Последнее редактирование: Июнь 18, 2014, 20:07 от 8Observer8 » Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4350



Просмотр профиля
« Ответ #23 : Июнь 18, 2014, 20:13 »

А что вас удивляет?
Вы коннект делаете в конструкторе, а объект получаете в методе fetch.
Записан
8Observer8
Гость
« Ответ #24 : Июнь 18, 2014, 20:20 »

Спасибо большое! Я как раз так попробовал - работает! Правда несколько удивляет. Получается, что конекты всегда надо делать только после того, как объекты полностью инициализирован?

Код
C++ (Qt)
   void fetch( const QString &url )
   {
       m_reply.reset(m_manager->get( QNetworkRequest( QUrl( url ) ) ) );
       connect( m_reply.get( ), SIGNAL( finished( ) ),
                this, SLOT( replyFinished( ) ) );
       connect( m_reply.get( ), SIGNAL( QNetworkReply::downloadProgress( qint64 bytesReceived, qint64 bytesTotal ) ),
                this, SLOT( slotDownloadProgress(qint64 bytesReceived, qint64 bytesTotal ) )     );
   }
 
Записан
8Observer8
Гость
« Ответ #25 : Июнь 18, 2014, 20:41 »

Ещё в моём коде надо заменить:
Код
C++ (Qt)
connect( m_reply.get( ), SIGNAL( QNetworkReply::downloadProgress( qint64 bytesReceived, qint64 bytesTotal ) ),
                this, SLOT( slotDownloadProgress(qint64 bytesReceived, qint64 bytesTotal ) ) );
 

На этот код:
Код
C++ (Qt)
connect( m_reply.get( ), SIGNAL( downloadProgress( qint64, qint64 ) ),
                this, SLOT( slotDownloadProgress(qint64, qint64 ) ) );
 

И ещё вопрос не совсем по теме. Этот модуль скачивает содержимое HTML страницы и я хотел бы видеть процесс скачивания, но у меня bytesTotal  всегда равен -1. Что делать?
Записан
vizir.vs
Гость
« Ответ #26 : Июнь 19, 2014, 08:23 »

я не понял, а зачем в make_shared new?
Код:
        std::shared_ptr<QNetworkAccessManager> manager =
                std::make_shared<QNetworkAccessManager>( new QNetworkAccessManager( this ) );

make_shared специально создан для того, чтобы не надо было вызывать new и не было ненужной аллокации.

Нужно писать так.
Код:
std::shared_ptr<QNetworkAccessManager> manager =
                std::make_shared<QNetworkAccessManager>(this);
Записан
8Observer8
Гость
« Ответ #27 : Июнь 19, 2014, 08:49 »

Спасибо Вам огромное! Вы мне показали и как инициализировать shared_ptr с помощью reset и как правильно инициализировать с помощью std::make_shared Улыбающийся
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #28 : Июнь 19, 2014, 10:14 »

Читал-читал, так и не понял чему это все посвящено Улыбающийся
Код
C++ (Qt)
private:
   QNetworkReply * m_reply;
};
Ну и в конструкторе инициализировать, в деструкторе сделать delete. Не вижу какие тут возможны (у)течки. Может кайф в том что шареный m_reply можно куда-то отдать а сам класс Downloader уже удалить. Но по приведенному коду не видно где это обыгрывается. Выходит, так, "на всякий случай" - засорить код и пустить пыль в глаза.
Записан
8Observer8
Гость
« Ответ #29 : Июнь 19, 2014, 10:17 »

Igors, смысл в том, чтобы везде использовать shared_ptr вместо обычных указателей.
Записан
Страниц: 1 [2] 3   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.472 секунд. Запросов: 23.