2012-02-21 1 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 в этот список. Еще один вопрос, потому что я хочу понять, что делает im: requestcontexes.push_front (cpContext), добавляет копию элемента в список, объявленный глобально? не ссылка на cpContext? –

+0

Да. Не забудьте использовать блокировку для защиты этого списка, так как к нему могут одновременно обращаться несколько потоков. –

0

Это зависит от того, что REQUEST_CONTEXT есть. Если это объект, то cpContext уничтожается. Если это указатель, тогда только указатель уничтожается.

Означает, что это фактический объект (потому что вы передаете адрес), основываясь на том, что вы нам дали, я бы сказал, что он уничтожается, когда заканчивается SendRequest(), и утечки памяти нет.

Не должно быть трудно проверить, не так ли?

+0

REQUEST_CONTEXT - это структура, содержащая буфер для входящих данных. Поэтому, возможно, мне нужно объявить его глобальным. Потому что, когда есть данные, доступные из HTTP-запроса (инициированного sendrequest), он будет уведомлять через обратный вызов –

0

Как и все локальные переменные, cpContext действительно выходит за рамки. Это не утечка памяти, но еще хуже. Этот обратный вызов будет использовать объект после его окончания жизни.

Если вы знаете 100%, конечно, что обратный вызов будет называться точно один раз, вы можете выделить cpContext с помощью new REQUEST_CONTEXT и удалить его из обратного вызова. Если вы не знаете, проблема намного хуже. Вы должны будете использовать API-специфические методы, чтобы выяснить, как долго объект должен оставаться в живых.

0

cpContext выделяется в стек как любая другая локальная переменная вашей функции SendRequest().

Ваше текущее решение хуже, чем утечка памяти. Дело не в том, что вы просачиваете память. Дело в том, что объект больше не существует, но вы по-прежнему удерживаете указатель на этот адрес.

С моей точки зрения, это неопределенное поведение. Не делай этого.

У вас есть следующие варианты, чтобы решить вашу проблему:

  1. использовать глобальную переменную для переменной cpContext.
  2. Выделите свой cpContext в кучу через новый.
  3. Выделите cpContext в другом контексте, в котором вы уверены, будет жить до завершения обратного вызова.