2012-02-21 116 views
0

后我有以下代码:C++可变功能结束

void SendRequest(HINTERNET connection, LPCWSTR method, LPCWSTR referer,LPCWSTR path,WINHTTP_STATUS_CALLBACK whCallback){  
    HINTERNET request; 
    request=WinHttpOpenRequest(connection, 0,path,0,referer,WINHTTP_DEFAULT_ACCEPT_TYPES,0); 
    WinHttpSetStatusCallback(request, (WINHTTP_STATUS_CALLBACK)whCallback,WINHTTP_CALLBACK_FLAG_ALL_NOTIFICATIONS,0); 
    REQUEST_CONTEXT cpContext; 
    WinHttpSendRequest(request,WINHTTP_NO_ADDITIONAL_HEADERS,0,NULL,NULL,NULL,(DWORD_PTR)&cpContext); 
}; 

WinHttpSendRequest不会阻塞,因此一旦它被执行时,函数结束。但是,WinHttpSendRequest以cpContext作为参数回调到另一个函数。所以我的问题是,doe cpContext在函数结束后被破坏?这是否会导致内存泄漏,因为无法在函数外部访问cpContext?我如何在最佳的C++实践中做到这一点?

回答

3

cpContext在函数结束时被销毁。这导致未定义的行为,因为在调用回调之前cpContext可能会被销毁。如果您在回调中取消引用指向它的指针,则这是未定义的行为。解决方法之一是避免使用本地范围的变量:

REQUEST_CONTEXT* cpContext = new REQUEST_CONTEXT(); 
WinHttpSendRequest(request,WINHTTP_NO_ADDITIONAL_HEADERS,0,NULL,NULL,NULL,(DWORD_PTR)cpContext); 

不要在回调函数中忘记delete它。

泄漏将是HINTERNET句柄,除非您在回调中关闭它。该句柄需要使用WinHttpCloseHandle来关闭,但在异步请求处于活动状态时无法关闭它。

+0

因此,为了解决这两个问题,我可以添加一个REQUEST_CONTEXT结构类型的全局列表,并将HINTERNETHANDLE移动到该列表。还有一个问题,因为我想了解我在做什么:requestcontexes.push_front(cpContext),将元素的副本添加到全局声明的列表中?不是对cpContext的引用? – 2012-02-21 15:20:04

+0

是的。不要忘记使用锁来保护这个列表,因为它可能被多个线程同时访问。 – 2012-02-21 15:23:20

0

这取决于REQUEST_CONTEXT是什么。如果这是一个对象,然后cpContext被破坏。如果它是一个指针,那么只是指针被破坏。

看着你的代码,它是一个实际的对象(因为你传递的是地址),所以基于你给我们的东西,我会说它在SendRequest()结束时被破坏,并且没有内存泄漏。

不应该很难测试,对吧?

+0

REQUEST_CONTEXT是一个结构,它包含传入数据的缓冲区。所以也许我需要全局声明它。因为当有来自http请求的数据(由sendrequest发起)时,它会通过回调通知 – 2012-02-21 15:00:59

0

与所有局部变量一样,cpContext确实超出了范围。这不是内存泄漏,但更糟。该回调将在其生命周期结束后使用对象。

如果你知道100%肯定的是,回调将被精确地调用一次,你可以用new REQUEST_CONTEXT分配cpContext,并从回调删除。如果你不知道,问题就更糟。您必须使用特定于API的方法来确定对象应该保持活动状态的时间。

0

将cpContext作为SendRequest()函数的其他本地变量分配到堆栈中。

您目前的解决方案更糟糕的是内存泄漏。这并不是说你正在泄漏记忆。事实是该对象不再存在,但您仍然持有指向该地址的指针。

从我的角度来看,这是未定义的行为。不要这样做。

您有以下选项来解决问题:

  1. 使用全局变量您cpContext变量。
  2. 通过new在堆上分配您的cpContext。
  3. 在另一个上下文中分配cpContext,您肯定会活下去,直到回调完成。