Article 10: Minimize the mixing of smart pointers and interface pointers
Before starting this section, let's take a look at an example:
View plaincopy to clipboardprint? Void func (void)
{
ICalculator * pCalculator = NULL;
CComPtr <ICalculator> pCalculator = NULL;
HrRetCode = CoCreateInstance (// create COM component here, reference count is 1.
CLSID_CALCULATOR,
NULL,
CLSCTX_INPROC_SERVER,
IID_ICALCULATOR,
(Void **) & pCalculator
);
Assert (hrRetCode );
SpCalculator = pCalculator; // assign the created component to the smart pointer to manage the reference count.
// The Value assignment operator calls AddRef () again to increase the reference count.
SpCalculator-> DoSomething ();
} // Resource leakage occurs when the reference count is not 0 after Release () is called.
Void func (void)
{
ICalculator * pCalculator = NULL;
CComPtr <ICalculator> pCalculator = NULL;
HrRetCode = CoCreateInstance (// create COM component here, reference count is 1.
CLSID_CALCULATOR,
NULL,
CLSCTX_INPROC_SERVER,
IID_ICALCULATOR,
(Void **) & pCalculator
);
Assert (hrRetCode );
SpCalculator = pCalculator; // assign the created component to the smart pointer to manage the reference count.
// The Value assignment operator calls AddRef () again to increase the reference count.
SpCalculator-> DoSomething ();
} // Resource leakage occurs when the reference count is not 0 after Release () is called.
Without careful observation, it may be difficult to find a resource leakage problem in the above example. However, after careful analysis, we can find that in the code above, CoCreateInstance calls the AddRef () of the interface once (). In the smart pointer assignment process, AddRef () is called again (). However, only one Release () call is required for smart pointer analysis. Therefore, the reference Technology of COM is not set to zero, and thus the structure will not be analyzed. However, after the function is completed, no pointer can access the COM component again. Therefore, it is "free" in the memory, and resource leakage occurs.
The key to the problem lies in the mixed use of interface pointers and smart pointers. In the end, the Release () of the interface pointer is forgotten, resulting in resource leakage.
You may add "pCalculator-> Release ()" at the end of this function, but we do not recommend that you do this. Because the purpose of introducing smart pointers is to simplify our development as much as possible. If you use smart pointers in combination with interface pointers, it takes more time to think about reference counting.
Therefore, the best solution is to completely remove the interface pointer from the function. The above functions can be completed in the following example:
View plaincopy to clipboardprint? Void func (void)
{
CComPtr <ICalculator> pCalculator = NULL; // only smart pointer
HrRetCode = pCalculator. CoCreateInstance (CLSID_CALCULATOR,); // The reference count is 1.
Assert (hrRetCode );
SpCalculator-> DoSomething ();
} // After Release () is called, the reference count is 0, and resources are correctly released.
Void func (void)
{
CComPtr <ICalculator> pCalculator = NULL; // only smart pointer
HrRetCode = pCalculator. CoCreateInstance (CLSID_CALCULATOR,); // The reference count is 1.
Assert (hrRetCode );
SpCalculator-> DoSomething ();
} // After Release () is called, the reference count is 0, and resources are correctly released.
The mixed use of smart pointers makes it difficult to predict the reference count of the Code. Therefore, we should not mix smart pointers and interface pointers in the code.
However, with all exceptions, interface pointers are not useless. And you have seen this question in the previous chapter. We still use the interface pointer when passing function parameters.
View plaincopy to clipboardprint? Void SomeApp (IHello * pHello)
{
CComPtr <IHello> pCopy = pHello;
OtherApp ();
PCopy-> Hello ();
}
Void SomeApp (IHello * pHello)
{
CComPtr <IHello> pCopy = pHello;
OtherApp ();
PCopy-> Hello ();
}
You may wonder why pHello in SomeApp (IHello * pHello) does not use smart pointers?
It is not impossible to place a smart pointer in a function parameter. We chose the interface pointer as the type of parameter transfer for the following considerations:
1. If a smart pointer is used, this interface cannot be described in the IDL file.
2. Using Smart pointers as parameter passing increases the complexity of the interface and makes it difficult to understand.
3. If you use a third-party smart pointer as the parameter type, you need to publish the implementation of this smart pointer along with the interface when releasing the interface. If you cannot implement this smart pointer, you cannot use this interface.
4. Smart pointers generate greater overhead during the transfer process, thus reducing program performance.
......
If a smart pointer appears in the return value of a function, the damage is even greater. Think about the following code and what will happen?
View plaincopy to clipboardprint? CComPtr <IView> GetView (int nIndex)
{
CComPtr <IView> spView = NULL;
SpView. CreateInstance (CLSID_MYCOMPONENT );
Return spView;
}
CComPtr <IView> GetView (int nIndex)
{
CComPtr <IView> spView = NULL;
SpView. CreateInstance (CLSID_MYCOMPONENT );
Return spView;
}
But what if the calling process is like this?
View plaincopy to clipboardprint? IView * pIView = NULL;
PIView = GetView ();
PIView-> DoSomething ();
IView * pIView = NULL;
PIView = GetView ();
PIView-> DoSomething ();
In the analysis process, GetView () returns a temporary object, which is a smart pointer and Its Reference count is 1. After it assigns this smart pointer to another interface pointer, it analyzes the structure. In this case, the reference count is 0, so the COM component is released. Then, as pIview calls the DoSomething () program, it crashes ~
Now you must be certain that a mix of smart pointers and interface pointers makes reference counting hard to comprehend. However, in function parameter passing and function return values, the existence of interface pointers is allowed.
Author's "liuchang5 column"