As we all know, smart pointers store a pointer in the form of T * m_Pointer. When the reference count is zero, m_Pointer is automatically deleted,
When T is not void, everyone will write it. But when T is void, the compiler will certainly report an error when compiling to delete m_Pointer. What should we do? I have been thinking about it for a long time before I can solve it with a clever way. In addition, this solution not only solves the void deletion problem, but also can be used for all types. The key to the solution lies in the member function pointer.
The delete operator cannot be converted into a member function, so a class is declared for encapsulation. For convenience of subsequent unified processing, this class will inherit from a class called VoidClass. The Code is as follows:
class VoidClass{public:VoidClass(){m_Pointer=0;}VoidClass(void* p){m_Pointer=p;}void* GetPointer(){return m_Pointer;}bool IsNull(){return m_Pointer==0?1:0;}private:void* m_Pointer;};
template
class Destructor:public VoidClass{public:Destructor(T* p):VoidClass(p){}void Do(){delete ((T*)GetPointer());}};
To save the member function pointer conveniently, use two typedef to define the member function type:
typedef void(Destructor
::*RawDestructFunc)();typedef void(VoidClass::*DestructFunc)();
The following idea is:
1. encapsulate any pointer into Destructor and convert Destructor to VoidClass for saving.
2. encapsulate the pointer into the Do function (that is, the actual Destructor) after the Destructor and convert it to the DestructFunc type for storage. During the delete operation, only the VoidClass object is used to call DestructFunc.
Some people say that the Do function does not appear in VoidClass. why can it be converted into a member function pointer of VoidClass. I don't go into this here. I guess the compiler only recognizes the address. As long as the pointer value remains the same and the address value of the member function pointer remains the same, any changes to its type can be called normally.
Add the following two member variables to the smart pointer class:
VoidClass* m_Destructor;DestructFunc m_DestructFunc;
Coincidentally, RawDestructFunc used a method that appeared in MFC when converting to DestructFunc, and used union to convert the type. I call it All_Cast. The Code is as follows:
template
union cast_union{InputType in;OutPutType out;};template
OutPutType All_cast(InputType a){cast_union
u;u.in=a;return u.out;}
The constructor is as follows:
template
amyKeeper(T1* p){typedef void(Destructor
::*RawDestructFunc)();Destructor
* dtor=new Destructor
(p);m_Destructor=(VoidClass*)(dtor);m_DestructFunc=All_cast
(&Destructor
::Do);m_Counter=new Counter; IncKeeper(); }
The Destructor is as follows:
(m_Destructor->*m_DestructFunc)(); delete m_Destructor; m_Destructor=0;
In this case, mySmartPointer p = new B; or mySmartPointer P = new B; B's destructor will be correctly called no matter what the structure is.
Because the constructor automatically recognizes the type of B:
Destructor
* dtor=new Destructor
(p);
No matter what the template of the mySmartPointer class is, the template parameter of the constructor is always B, so the destructor of Class B is correctly converted to m_DestructFunc and saved.
You can construct some simple code to verify whether the Destructor is correctly called.