[英]QNetworkReply wrapper stored into QList makes program finish unexpectedly?
In order to learn Qt I'm testing some Qt (5.3.1) classes; 为了学习Qt,我正在测试一些Qt(5.3.1)类。 for now I'm testing some of the classes provided on the network package such as QNetworkAccessManager
, QNetworkReply
, etc... 目前,我正在测试网络程序包上提供的一些类,例如QNetworkAccessManager
, QNetworkReply
等。
I'm using Qt creator (3.1.2) to compile a simple program which uses the following QNetworkReply
wrapper: 我正在使用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;
};
All the wrapped network replies are stored on a QList
named R
: 所有包装的网络答复都存储在名为R
的QList
:
QList<Request> R;
And are created through the following function: 并通过以下功能创建:
void get(const QString &a_url) {
QNetworkRequest request(a_url);
R.push_back(NetworkAccessManager.get(request));
}
The NetworkAccessManager
is an instance of QNetworkAccessManager
which lies outside the main
function (the same goes for the list R
): 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();
}
The program pasted above, produces the following output: 上面粘贴的程序将产生以下输出:
Build 28fdc8 1be843b8
Destroy 28fdc8 1be843b8
Build 28fdc8 1be84558
Destroy 28fdc8 1be84558
Build 28fdc8 1be84638
Destroy 28fdc8 1be84638
So, it seems that the same instance of Request
is managing different QNetworkReply
pointers (the this
pointer printed is the same while the QNetworkReply
changes) and no "Reply!"
因此,似乎同一Request
实例正在管理不同的QNetworkReply
指针(当QNetworkReply
更改时, this
指针的输出是相同的),并且没有"Reply!"
nor "Error!"
也不是"Error!"
is shown; 显示; after click on the close button of the application window, the program crashes about 8 or 10 seconds later printing: 单击应用程序窗口的关闭按钮后,该程序在打印大约8或10秒后崩溃:
The program has unexpectedly finished. 该程序意外完成。 C:\\Code\\build-test-Desktop_Qt_5_3_MinGW_32bit-Debug\\debug\\test.exe crashed C:\\ Code \\ build-test-Desktop_Qt_5_3_MinGW_32bit-Debug \\ debug \\ test.exe崩溃
Removing the deleteLater()
instruction on ~Request
produces the following output: 卸下deleteLater()
上指令~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
And the program does not crash anymore, but it seems that a copy of Request
is made somewhere for each get
call according to the 3 exttra Destroy
messages. 该程序不会再崩溃,但是根据3个额外的Destroy
消息,似乎在每个get
调用的某处都复制了Request
的副本。 And to make things worse I'm not doing the deleteLater()
anymore ( which seems to be mandatory ), so I've placed the deleteLater()
at the end of the done
function: 更糟糕的是,我不再使用deleteLater()
了( 这似乎是必需的 ),因此我将deleteLater()
放在了done
函数的末尾:
void done(QNetworkReply *reply) {
// do stuf...
reply->deleteLater();
}
With good results (no application crash), but I'm worried because I don't get what's happening, so I wonder if any of you would be so kind to answer the following questions: 结果不错(没有应用程序崩溃),但是我担心,因为我不了解发生了什么,所以我想知道你们中的任何一个人是否愿意回答以下问题:
done
isn't called if the deleteLater()
is placed in ~Request
? 为什么功能done
,如果不叫deleteLater()
被放置在~Request
? done
( "Reply!"
output) is done AFTER the call to ~Request
( "Destroy"
output)? 为什么在调用~Request
( "Destroy"
输出)之后调用done
( "Reply!"
"Destroy"
输出)? this
of Request
(printed on construction) have the same value on each call? 为什么Request
的指针this
(打印在构造上)在每个调用中都具有相同的值? (maybe due to some optimization?). (也许是由于某些优化?)。 Request
is made? 在何处(为什么)复制Request
? deleteLater()
of each QNetworkReply
. 调用每个QNetworkReply
的deleteLater()
的最佳位置在哪里? Thanks. 谢谢。
The Andrew Medico comment doesn't answer any of my questions but pointed me on the right direction. Andrew Medico的 评论没有回答我的任何问题,但为我指明了正确的方向。
In fact, according to the documentation linked by Andrew (emphasis mine): 实际上,根据安德鲁(我的重点)链接的文档:
The values stored in the various containers can be of any assignable data type. 存储在各个容器中的值可以是任何可分配的数据类型。 To qualify, a type must provide a default constructor, a copy constructor, and an assignment operator . 要获得资格,类型必须提供默认构造函数,副本构造函数和赋值运算符 。
And so it did my Request
struct (implicitly not explicity, that's Ok for me though), but in order to accomplish all the must of the QList
contained type I've decided to provide the missing parts: 因此,它执行了我的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;
};
After that, the output changed and all becomes crystal clear: 在那之后,输出改变了,一切变得清晰起来:
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
So, let's answer the questions: 因此,让我们回答以下问题:
Why the function
done
isn't called if thedeleteLater()
is placed in~Request
? 为什么功能done
,如果不叫deleteLater()
被放置在~Request
?
Every time a new Request
is pushed into the R
container ( R.push_back(NetworkAccessManager.get(request))
) happens the following: 每次将新Request
推送到R
容器( R.push_back(NetworkAccessManager.get(request))
)时,都会发生以下情况:
Request
object is created ( "Build"
output). 创建一个R值Request
对象( "Build"
输出)。 R
via copy constructor ( "Copy"
output). R值通过复制构造函数插入到R
( "Copy"
输出)。 "Destroy"
output). 从中复制的R值被销毁( "Destroy"
输出)。 The destruction performed on the 3rd step causes all the problems: because the QNetworkReply
pointer managed by the R-value and the copied contained Request
are the same so, calling the deleteLater()
placed in ~Request
causes that the contained Request
starts to manage a deleted (or future-deleted) resource! 在第三步骤中进行的破坏会导致所有的问题:因为QNetworkReply
由R值和复制包含的管理指针Request
是相同的,所以,在调用deleteLater()
置于~Request
使所包含的Request
开始管理一个删除(或将来删除)的资源! That's why done
isn't called, the fact is that there's no QNetworkReply
to wait to complete (it was deleted!). 这就是为什么不调用done
的原因,事实是没有等待完成的QNetworkReply
(已删除!)。
It's kind of a shame that the QList doesn't provides an emplace_back
. QList没有提供emplace_back
。
Why the call to
done
("Reply!"
output) is done AFTER the call to~Request
("Destroy"
output)? 为什么在调用~Request
("Destroy"
输出)之后调用done
("Reply!"
"Destroy"
输出)?
The "Destroy"
output corresponds to the destruction of the R-value created in order to insert a Request
into the R
container, the "Reply!"
"Destroy"
输出对应于为将Request
插入R
容器而创建的R值的销毁,即"Reply!"
output happens after the insertion, so it's after the destruction of the R-value and then, the contained Request
is destroyed ( "Destroy"
output AFTER the "Reply!"
output). 输出发生在插入之后,因此是在销毁R值之后,然后销毁所包含的Request
( "Destroy"
输出之后的"Reply!"
"Destroy"
"Reply!"
输出)。
Why the pointer
this
ofRequest
(printed on construction) have the same value on each call? 为什么Request
的指针this
(打印在构造上)在每个调用中都具有相同的值? (maybe due to some optimization?). (也许是由于某些优化?)。
The repeated address corresponds to the address of the R-value used to populate the R
container, my guess is that the same address is used to construct this R-value at each call as a some kind of optimization. 重复的地址对应于用于填充R
容器的R值的地址,我猜是作为某种优化,每次调用时都使用相同的地址来构造此R值。
Where (and why) a copy of a
Request
is made? 在何处(为什么)复制Request
?
See the first answer. 请参阅第一个答案。
Where is the best location to call the
deleteLater()
of eachQNetworkReply
. 调用每个QNetworkReply
的deleteLater()
的最佳位置在哪里?
At the end of the function done
should be Ok. 在功能done
应该可以。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.