2013-03-14 11 views
8

Podczas korzystania z modelu COM zazwyczaj korzystam z inteligentnych wskaźników ATL, takich jak ATL::CComPtr i ATL::CComBSTR, w celu zarządzania zasobami. Ale niektóre z wywoływanych przeze mnie metod używają parametrów wyjściowych do zwracania wskaźników do przydzielonego miejsca, które muszę zwolnić. Na przykład:Obsługa pamięci o wyjątkowych właściwościach z COM

WCHAR *pszName = nullptr; 
if (SUCCEEDED(pShellItem->GetDisplayName(SIGDN_FILESYSPATH, &pszName))) { 
    DoSomething(pszName); 
    CoTaskMemFree(pszName); 
} 

Zauważ, że GetDisplayName przydziela pamięć dla łańcucha i zwraca wskaźnik do niego za pośrednictwem parametru wyjściowego. Obowiązkiem osoby wywołującej jest zwolnienie tej pamięci za pomocą CoTaskMemFree.

Jeśli DoSomething zgłasza wyjątek, powyższy kod wycieknie. Chciałbym użyć jakiegoś inteligentnego wskaźnika dla pszName, aby uniknąć takich przecieków, ale API bierze WCHAR**, więc nie widzę, jak mogę przekazać cokolwiek innego niż adres niemądrego wskaźnika. Ponieważ nie jestem tym, który przydziela, nie mogę używać RAII.

I może użytkowania RRID czy mogę zrobić Deleter tak:

struct CoTaskMemDeleter { 
    void operator()(void *p) { ::CoTaskMemFree(p); } 
}; 

a następnie natychmiast przypisać zwrócony wskaźnik do standardowej inteligentnego wskaźnika jak ten:

WCHAR *pszName = nullptr; 
if (SUCCEEDED(pShellItem->GetDisplayName(SIGDN_FILESYSPATH, &pszName))) { 
    std::unique_ptr<WCHAR, CoTaskMemDeleter> guard(pszName); 
    DoSomething(pszName); 
} 

który działa , ale wydaje się być podatnym na błędy wprowadzenie dodatkowej zmiennej ochronnej. Na przykład to podejście pozostawia pszName wskazując na zwolnioną pamięć, aby łatwo było z niej przypadkowo skorzystać.

Czy istnieje czyściejszy sposób użycia inteligentnego wskaźnika lub opakowania RAII dla pamięci przydzielonej serwerowi COM zwróconej przez parametr wyjściowy? Czy brakuje mi czegoś, co zapewnia ATL?

+0

Co jest złego w uwalnianiu pamięci? Dla mnie nie wydaje się, by warto było robić cokolwiek innego. – evanmcdonnal

+0

@evanmcdonnal: bezpieczeństwo wyjątków. –

Odpowiedz

11

ATL ma już ten jeden na polu:

CComHeapPtr<WCHAR> pszName; 
const HRESULT nResult = pShellItem->GetDisplayName(..., &pszName); 
// Hooray, pszName will be CoTaskMemFree'd for you on scope leave 
// via ~CComHeapPtr 

CHeapPtr można wyprowadzić wdrożyć inne środki uwalniające zasobów w podobny sposób. CComHeapPtr to MSDN documented class.

+1

Widziałem CComHeapPtr w dokumentach ATL, ale nie zdawałem sobie sprawy, że 'CoTaskMemAlloc' /' Free' używa 'CComAllocator'. Następną moją reakcją było "Jak to się kompiluje?", Ponieważ '& pszName' wydaje się być' CComHeapPtr * 'zamiast' WCHAR ** '. Następnie odkryłem, że klasy wskaźników ATL przeciążają jednoargumentowy 'operator &', który uważałem za zabroniony, ale jest tak naprawdę odradzany. To przeciążenie uniemożliwia również zachowanie ich w kontenerze STL. –

+0

Klasa inteligentnego szablonu jest podobna do dobrze znanego 'CComPtr' przez zawijanie wskaźnika i wzięcie jednego odniesienia' * 'z argumentu szablonu:' IUnkown * 'jest zawijany przez' CComPtr ', a' WCHAR * 'jest zawijany przez' CHeapPtr '. Tak, oba przeciążają '&' w szczególności w celu zwiększenia błędu asercji, gdy próbujesz wystawić wskaźnik inny niż NULL jako symbol zastępczy, aby zaakceptować nową wartość nieprzetworzoną. –

+1

Po prostu z ciekawości: Kto zniechęca operatora do pisania? – StuartRedmann

Powiązane problemy