// HandleTest2.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <Windows.h> #include <iostream> using namespace std; #include "SmartHandle.h" #include "SmartPtr.h" #include "TestClass.h" // Testing the CAutoHandle template class. // Additional feature: misusing of handle wrapper as pointer // is not compiled. int _tmain(int argc, _TCHAR* argv[]) { // test smart handles CAutoLibrary hLibrary; hLibrary = LoadLibrary(_T("psapi.dll")); CAutoGeneralHandle hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); SetEvent(hEvent); // hLibrary->unused = 0; // not compiled, exactly as needed // test smart pointers CAutoPointer<CTestClass>::AutoPtr pTest = new CTestClass(); pTest->DoSomething(); CAutoArrayPointer<CTestClass>::AutoPtr pArrayTest = new CTestClass[2]; return 0; }
// SmartPtr.h#pragma once// Additional release algorithms which allow to use// CSmartHandle as smart pointer// *******************************************************************************// Release algorithm for plain pointer.// Using this release algorithm CSmartHandle works// like simple smart pointer.template <typename T, class PointedBy>struct CClosePointer{ void Close(T handle) { delete (PointedBy*)handle; }protected: ~CClosePointer() { }};// I want to make typedef for using CSmartHandle with CClosePointer policy.// However, I cannot do this because user needs to provide class name parameter.// I can use define:// #define CAUTOPOINTER(ClassName) CSmartHandle<ClassName*, CClosePointer, ClassName>// Client code is:// CAUTOPOINTER(CTestClass) pTestClass = new CTestClass();//// but more elegant solution is constructing new type:template <class T>struct CAutoPointer{ typedef CSmartHandle<T*, CClosePointer, T> AutoPtr;};// Client code is:// CAutoPointer<CTestClass>::AutoPtr pTestClass = new CTestClass();//// the struct CAutoPointer itself has no members apart from the type definition // and isn't instantiated, it's only used to provide the type member // which returns the type I need.// *******************************************************************************// Release algorithm for plain array.template <typename T, class PointedBy>struct CCloseArrayPointer{ void Close(T handle) { delete[] (PointedBy*)handle; }protected: ~CCloseArrayPointer() { }};//#define CAUTOARRAYPOINTER(ClassName) CSmartHandle<ClassName*, CCloseArrayPointer, ClassName>template <class T>struct CAutoArrayPointer{ typedef CSmartHandle<T*, CCloseArrayPointer, T> AutoPtr;};
在寫代碼時候,經常會將一個Handle關閉多次,或者不關閉,包括內存釋放也同樣.
可能大部分人覺得對一個handle關閉多次不會有問題, 但在管家項目中,就出現這樣的Crash.
爲了防止這樣的問題: 就必須使用智能指針.
// SmartHandle.h
#pragma once
// Class CSmartHandle works like Jeffrey Richter's EnsureCleanup class
// (Windows HANDLE wrapper which releases handle in destructor).
// The change: instead of release function pointer it uses template
// class (using the same technique as described in
// Modern C++ Design by Andrei Alexandrescu.
//
// Classes CCloseHandle, CCloseRegKey... are releasing policies.
// These classes are templates. Class CSmartHandle has second parameter
// ReleaseAlgorithm which is release policy template.
// This means, CSmartHandle template declaration contains
// nesting template. This code is compiled in Visual Stidio .NET 2003
// but not compiled in previous versions.
//
// Protected destructors prevent client code to release CSmartHandle
// using release pointer code (see Modern C++ Design,
// 1.7 Destructors of Policy Classes).
//
// Additional PointedBy algorithm prevents misusing of the smart handle
// instance as smart pointer (using -> with handle is not compiled).
// Release algorithms (release policies)
template <typename T, class PointedBy>
struct CCloseHandle
{
void Close(T handle)
{
cout << "Handle is released" << endl; // for testing
CloseHandle(handle);
}
protected:
~CCloseHandle()
{
}
};
template <typename T, class PointedBy>
struct CCloseRegKey
{
void Close(T handle)
{
RegCloseKey(handle);
}
protected:
~CCloseRegKey()
{
}
};
template <typename T, class PointedBy>
struct CCloseLibrary
{
void Close(T handle)
{
cout << "Library is released" << endl; // for testing
FreeLibrary(handle);
}
protected:
~CCloseLibrary()
{
}
};
template <typename T, class PointedBy>
struct CCloseViewOfFile
{
void Close(T handle)
{
UnmapViewOfFile(handle);
}
protected:
~CCloseViewOfFile()
{
}
};
// Empty class used as default CAutoHandle template parameter.
class CEmptyClass
{
};
// Class CSmartHandle which implements release policy.
// Second template parameter is ReleaseAlgorithm which is template itself.
template <typename HandleType,
template <class, class> class ReleaseAlgorithm,
class PointedBy = CEmptyClass, // used for smart pointers
HandleType NULL_VALUE = NULL>
class CSmartHandle : public ReleaseAlgorithm<HandleType, PointedBy>
{
public:
CSmartHandle()
{
m_Handle = NULL_VALUE;
}
CSmartHandle(HandleType h)
{
m_Handle = h;
}
HandleType operator=(HandleType h)
{
CleanUp();
m_Handle = h;
return(*this);
}
operator HandleType()
{
return m_Handle;
}
PointedBy* operator->() // for using as smart pointer
{
// NOTE: adding this operator allows to use CAutoHandle object as pointer.
// However, if PointedBy is CHandlePlaceHolder (used for handles),
// this is not compiled because CHandlePlaceHolder has no functions.
// This is exactly what I need.
return m_Handle;
}
BOOL IsValid()
{
return m_Handle != NULL_VALUE;
}
~CSmartHandle()
{
CleanUp();
}
protected:
void CleanUp()
{
if ( m_Handle != NULL_VALUE )
{
Close(m_Handle);
m_Handle = NULL_VALUE;
}
}
HandleType m_Handle;
};
// Client code (definitions of standard Windows handles).
typedef CSmartHandle<HANDLE, CCloseHandle> CAutoGeneralHandle;
typedef CSmartHandle<HKEY, CCloseRegKey> CAutoRegKey;
typedef CSmartHandle<PVOID, CCloseViewOfFile> CAutoViewOfFile;
typedef CSmartHandle<HMODULE, CCloseLibrary> CAutoLibrary;
typedef CSmartHandle<HANDLE, CCloseHandle, CEmptyClass,
INVALID_HANDLE_VALUE> CAutoFile;