为了学习Qt,我测试了一些Qt(5.3.1)课程;现在我正在测试网络包上提供的一些类,例如QNetworkAccessManager
,QNetworkReply
等......
我使用Qt creator(3.1.2)编译一个使用以下QNetworkReply
包装器的简单程序:
struct Request {
Request(QNetworkReply *const &a_reply) : m_reply(a_reply) {
qDebug() << "Build " << hex << int(this) << hex << int(m_reply);
}
~Request() {
if (m_reply) m_reply->deleteLater();
qDebug() << "Destroy" << hex << int(this) << hex << int(m_reply);
}
QNetworkReply *m_reply;
};
所有包装的网络回复都存储在名为QList
的{{1}}:
R
通过以下功能创建:
QList<Request> R;
void get(const QString &a_url) {
QNetworkRequest request(a_url);
R.push_back(NetworkAccessManager.get(request));
}
是NetworkAccessManager
的一个实例,位于QNetworkAccessManager
函数之外(列表main
也是如此):
R
上面粘贴的程序产生以下输出:
QList<Request> R;
QNetworkAccessManager NetworkAccessManager;
void done(QNetworkReply *reply) {
if (reply->error() == QNetworkReply::NoError) {
qDebug() << "Reply!" << reply->readAll();
} else {
qDebug() << "Error!" << reply->errorString();
}
for (QList<Request>::iterator request = R.begin(), lastRequest = R.end(); request != lastRequest; ++request) {
if (request->m_reply == reply) {
R.erase(request);
}
}
}
int main(int argc, char *argv[]) {
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
// Default "hello world" qml file:
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
QObject::connect(&NetworkAccessManager, &QNetworkAccessManager::finished, done);
get("http://us.battle.net/api/wow/achievement/2144");
get("http://us.battle.net/api/wow/achievement/2145");
get("http://us.battle.net/api/wow/achievement/2146");
return app.exec();
}
因此,似乎Build 28fdc8 1be843b8
Destroy 28fdc8 1be843b8
Build 28fdc8 1be84558
Destroy 28fdc8 1be84558
Build 28fdc8 1be84638
Destroy 28fdc8 1be84638
的同一个实例正在管理不同的Request
指针(QNetworkReply
指针打印的内容与this
更改时相同)且不{ {1}}也显示QNetworkReply
;单击应用程序窗口的关闭按钮后,程序将在8或10秒后崩溃打印:
该程序意外完成。 C:\ Code \ build-test-Desktop_Qt_5_3_MinGW_32bit-Debug \ debug \ test.exe崩溃
删除"Reply!"
上的"Error!"
指令会产生以下输出:
deleteLater()
该程序不再崩溃,但似乎根据3个exttra ~Request
消息,为每个Build 28fdc8 1be78078
Destroy 28fdc8 1be78078
Build 28fdc8 1be78218
Destroy 28fdc8 1be78218
Build 28fdc8 1be782e8
Destroy 28fdc8 1be782e8
Reply! "the expected json for http://us.battle.net/api/wow/achievement/2145"
Destroy 1be782c8 1be78218
Reply! "the expected json for http://us.battle.net/api/wow/achievement/2146"
Destroy 1be78398 1be782e8
Reply! "the expected json for http://us.battle.net/api/wow/achievement/2144"
Destroy 1be781f8 1be78078
调用在某处创建了Request
的副本。更糟糕的是,我不再使用get
which seems to be mandatory了,所以我已将Destroy
放在deleteLater()
的末尾功能:
deleteLater()
取得了良好的效果(没有应用程序崩溃),但我很担心,因为我没有得到正在发生的事情,所以我想知道你们中是否有人会这么善意回答以下问题:
done
放在void done(QNetworkReply *reply) {
// do stuf...
reply->deleteLater();
}
中,为什么不调用函数done
?deleteLater()
(~Request
输出)后完成对done
("Reply!"
输出)的调用?~Request
(构建时打印)的指针"Destroy"
在每个调用中具有相同的值? (可能是由于一些优化?)。this
的副本(及其原因)在哪里?Request
的{{1}}的最佳位置在哪里。感谢。
答案 0 :(得分:0)
Andrew Medico comment没有回答我的任何问题,但指出了正确的方向。
事实上,根据Andrew(强调我的)链接的文档:
存储在各种容器中的值可以是任何可分配的数据类型。要获得限定,类型 必须提供默认构造函数,复制构造函数和赋值运算符。
所以它完成了我的Request
结构(隐含地没有表达,但对我来说没问题),但为了完成QList
包含类型的所有必须我决定提供缺少的部分:
struct Request {
Request() :
m_reply(nullptr) {
qDebug() << "Default" << hex << int(this) << hex << int(m_reply);
}
Request(QNetworkReply *const &a_reply) :
m_reply(a_reply) {
qDebug() << "Build " << hex << int(this) << hex << int(m_reply);
}
Request(const Request &a_request) :
m_reply(a_request.m_reply) {
qDebug() << "Copy " << hex << int(this) << hex << int(m_reply);
}
Request &operator =(const Request &a_request) {
qDebug() << "Assign " << hex << int(this) << hex << int(m_reply = a_request.m_reply); return *this;
}
~Request() {
qDebug() << "Destroy" << hex << int(this) << hex << int(m_reply);
}
QNetworkReply *m_reply;
};
之后,输出发生了变化,一切都变得清晰:
Build 28fdc8 1ba5bda0
Copy 1ba5bf20 1ba5bda0
Destroy 28fdc8 1ba5bda0
Build 28fdc8 1ba5bf40
Copy 1ba5bff0 1ba5bf40
Destroy 28fdc8 1ba5bf40
Build 28fdc8 1ba5c020
Copy 1ba5c0c0 1ba5c020
Destroy 28fdc8 1ba5c020
Reply! "the expected json for http://us.battle.net/api/wow/achievement/2145"
Destroy 1ba5bff0 1ba5bf40
Reply! "the expected json for http://us.battle.net/api/wow/achievement/2144"
Destroy 1ba5bf20 1ba5bda0
Reply! "the expected json for http://us.battle.net/api/wow/achievement/2146"
Destroy 1ba5c0c0 1ba5c020
所以,让我们回答一下问题:
如果
done
放在deleteLater()
中,为什么不调用函数~Request
?
每次将新Request
推入R
容器(R.push_back(NetworkAccessManager.get(request))
)时,都会发生以下情况:
Request
对象("Build"
输出)。R
输出)插入"Copy"
。"Destroy"
输出)。在第3步执行的销毁会导致所有问题:因为由R值管理的QNetworkReply
指针和复制的包含Request
是相同的,所以调用deleteLater()
放在~Request
中导致包含的Request
开始管理已删除(或将来删除)的资源!这就是为什么done
没有被调用的原因,事实是没有QNetworkReply
等待完成(它被删除了!)。
令人遗憾的是QList没有提供emplace_back
。
为什么在调用
done
("Reply!"
输出)之后调用~Request
("Destroy"
输出)?
"Destroy"
输出对应于为了将Request
插入R
容器而创建的R值的销毁,插入后发生"Reply!"
输出,所以它是在R值被破坏之后,然后,包含的Request
被销毁("Destroy"
输出后"Reply!"
输出。
为什么
this
(构建时打印)的指针Request
在每个调用中具有相同的值? (可能是由于一些优化?)。
重复地址对应于用于填充R
容器的R值的地址,我的猜测是在每次调用时使用相同的地址构造此R值作为某种优化
Request
的副本在哪里(和为什么)?
见第一个答案。
调用每个
deleteLater()
的{{1}}的最佳位置在哪里。
在函数QNetworkReply
结束时应该没问题。