在局部函数内使用动态分配而又不影响信号/插槽机制时,如何处理内存泄漏。我正在使用将请求发送到服务器的客户端应用程序
这是我的情况:
MyClientApplication::sendRequestToServer()
{
QString url_string = http://..... // a url request
QUrl url = url_string;
QNetworkAccessManager *qnam = new QNetworkAccessManager();
connect(qnam, SIGNAL(finished(QNetworkReply *)), this, SLOT(handleResponseFromServer(QNetworkReply *)));
QNetworkRequest request(url);
QNetworkReply *reply = qnam->post(request);
}
我每次在客户端应用程序的成员函数中创建许多此类请求,每次都使用
QNetworkAccessManager
对象,该对象是动态创建的,用于向服务器发送请求。由于
qnam
的动态分配,如何处理内存泄漏?如果仅在堆栈上创建qnam
对象,那么当sendRequestToServer()
从调用返回时,信号/插槽机制将如何工作?我想在不损害信号/插槽机制的情况下解决内存泄漏问题。
最佳答案
有几种方法可以解决此问题,并且很难说出哪种方法最适合您的特定情况,而又不能更全面地了解用例。
但是,如果使用指针或堆栈对象,则可能对connect
机制很重要,这是您的困惑。您唯一需要确保的是对象生命周期足够好。
这意味着,该对象必须在需要时可用,例如,在离开期望的范围之后,该对象仍然必须存在。它可以通过堆以及堆栈对象来实现。
Qt父子层次结构
您可以简单地使MyClientApplication
QObject子类(直接或间接)成为网络访问管理器的父类。这样,可以确保仅在父级(即MyClientApplication对象停止存在)时破坏子级。请注意,在这种情况下,离开相应方法的范围时对象不会被破坏,这就是为什么它可以工作的原因。稍后将根据上述规则将其删除。
这是必要的代码更改:
MyClientApplication::sendRequestToServer()
{
QString url_string = http://..... // a url request
QUrl url = url_string;
QNetworkAccessManager *qnam = new QNetworkAccessManager(this); // This is how you set up the parent on the child
connect(qnam, SIGNAL(finished(QNetworkReply *)), this, SLOT(handleResponseFromServer(QNetworkReply *)));
QNetworkRequest request(url);
QNetworkReply *reply = qnam->post(request); // Naturally, you will need the stack object syntax here, respectively
}
This will probably be the easiest option for you to use based on the knowledge of your current code, so stick to this.
堆栈上的成员变量
您可以简单地使用在构造过程中初始化的堆栈对象。这将具有上述必要的生命周期,因为它仅在销毁
MyClientApplication
类期间被销毁。这是必要的代码更改:
class MyClientApplication...
{
...
QNetworkAccessManager qnam; // This is the new member variable
...
};
MyClientApplication::sendRequestToServer()
{
QString url_string = http://..... // a url request
QUrl url = url_string;
// Here you need to get the address of the object since the connect function expects a pointer
connect(&qnam, SIGNAL(finished(QNetworkReply *)), this, SLOT(handleResponseFromServer(QNetworkReply *)));
QNetworkRequest request(url);
QNetworkReply *reply = qnam.post(request); // Naturally, you will need the stack object syntax here, respectively
}
This would probably be the easiest option when you cannot use the Qt parent-child hierarchy.
堆上的成员变量
您可以简单地使用在构建期间创建并在构建期间销毁的堆对象。这将具有上述必要的生命周期,因为它仅在销毁
MyClientApplication
类期间被销毁。这是必要的代码更改:
class MyClientApplication...
{
...
QNetworkAccessManager *qnam; // This is the new member variable
...
};
MyClientApplication::MyClientApplication(...)
: qnam(new QNetworkAccessManager())
...
{
...
}
MyClientApplication::~MyClientApplication()
{
delete qnam;
qnam = 0;
...
}
MyClientApplication::sendRequestToServer()
{
QString url_string = http://..... // a url request
QUrl url = url_string;
// Construction removed here since that is done in the constructor
connect(qnam, SIGNAL(finished(QNetworkReply *)), this, SLOT(handleResponseFromServer(QNetworkReply *)));
QNetworkRequest request(url);
QNetworkReply *reply = qnam->post(request);
}
This is a bad practice in C++ in general to deal with the raw pointer explicitly. In certain cases, it makes sense, but if you do not have extra memory and performance concerns, this is not the way to go for. It is also error-prone if you forget to delete the raw pointer yourself.
具有智能指针的堆上的成员变量
您可以简单地使用在构建期间创建并在构建期间销毁的堆对象。这将具有上述必要的生命周期,因为它仅在销毁
MyClientApplication
类期间被销毁。您将使用智能指针为您处理自动销毁。这是必要的代码更改:
class MyClientApplication...
{
...
QPointer<QNetworkAccessManager> qnam; // This is the new member variable
...
};
MyClientApplication::MyClientApplication(...)
: qnam(new QNetworkAccessManager())
...
{
...
}
MyClientApplication::sendRequestToServer()
{
QString url_string = http://..... // a url request
QUrl url = url_string;
// Construction removed here since that is done in the constructor
connect(qnam, SIGNAL(finished(QNetworkReply *)), this, SLOT(handleResponseFromServer(QNetworkReply *)));
QNetworkRequest request(url);
QNetworkReply *reply = qnam->post(request);
}
This is a good practice in C++ in general to deal with pointers due to the simplified maintenance. However, if you have a simple code as you pasted, this is probably an overengineered solution.
希望这一详尽的解释有助于避免混淆。