Clause 4: Understand the simple and efficient recommendation of ATL CComPtr
After Microsoft launched the com sdk, it was very difficult to directly use the SDK to develop COM. So the first thing he did was to integrate COM into MFC. However, with the development of the Internet, distributed components require that COM be transmitted over the network, however, this has brought a considerable obstacle to the development of COM components by the MFC-the MFC is bloated, huge, and dependent on a lot of DLL files. In this case, ATL is born.
ATL [2] is the abbreviation of ActiveX Template Library. Different from MFC, ATL adopts the high-order programming skills such as multi-inheritance and template. Developers can not only quickly develop efficient and concise code, but also develop more lightweight and compact components. However, the addition of templates and multi-inheritance makes learning ATL more complex.
CComPtr is a class template provided by ATL to solve the problem caused by COM reference count. So you may understand. Smart pointers need to be implemented through templates (most smart pointers are designed in this way considering versatility, but there are also a few exceptions ). Therefore, you seem to understand that "smart Pointers" are just a simple alias for a specific template. It doesn't make people feel unattainable because of the use of advanced C ++ programming skills such as templates. At the same time, its usage and behavior are more similar to an interface pointer.
In addition to CComPtr, ATL also provides a smart pointer named CComQiPtr. The template classes of the two smart pointers are used to automatically manage the reference count of the COM interface and are declared in <atlbase. h>.
These two template classes are inherited from CComPtrBase. The difference is that CComQiPtr can automatically query the required interfaces when necessary (for example, when assigning values to pointers different from the smart pointer parameterization type, will automatically query whether there is a required interface ). The CComPtrBase class encapsulates most common functions in CComPtr and CComQiPtr to reuse code. The inheritance relationships of these three template classes are displayed:
It is worth noting that if your VC compiler version is too old (such as vc6.0), CComQiPtr cannot be used in ATL, and the base class CComPtrBase cannot be seen. Only CComPtr stays in the header file <atlbase. h>. This is because in the early ATL, the designer only designed a template class such as CComPtr. Later, the code of CComPtr and CComQiPtr will be extracted and put into the base class CComPtrBase to add new functions.
Therefore, this article only discusses CComPtr and its base class CComPtrBase, and does not involve CComQiPtr content (interested readers can refer to MSDN or read the ATL source code to learn more about it ). In CComPtr, most functions except constructors and value assignment operators are derived from CComPtrBase. To facilitate reading and understanding, we will discuss member functions in these two classes.
Let's take a look at the use of CComPtr. Maybe after some introduction, you will have a deeper understanding of how to manage the reference count inside him:
Take IHello * as an example. Use CComPtr <IHello> instead of all interface pointer types (except parameters) in the program. That is to say, in addition to parameters in the program, do not use IHello *, all of which are replaced by CComPtr <IHello>.
As follows:
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 ();
}
Finally, it is worth mentioning that although the usage of CComPtr is similar to that of common COM interface pointers, the following problems are still required:
1. CComPtr has ensured that AddRef and Release are called correctly, so it is not necessary or can no longer call AddRef and Release.
2. If you want to release a smart pointer, just assign it a NULL value.
3. the COM pointer is released when the CComPtr destructor is created.
4. When using the & operator (pointer address) for CComPtr, make sure that CComPtr is NUL. (Because AddRef is not automatically called when CComPtr is assigned a value through the CComPtr address. If it is not NULL, the previous pointer cannot be released, and CComPtr will use assert for alarm)
ATL pursues conciseness and efficiency. Therefore, when solving a problem, CComPtr will not solve the problem in all aspects. For example, if one method can solve this problem, ATL tries its best not to use the second method (unless the two methods are indeed different ).
From the ATL constructor, we can see that there are only four constructor: one necessary default constructor and one necessary copy constructor. There are also two conversion constructors to satisfy the pointer syntax. In this way, "proper benefits" are enough. As for the component creation process and interface Query Process, ATL believes that all these things can be completed by Constructing smart pointers. As follows:
View plaincopy to clipboardprint? CComPtr <ICalculator> spCalculator = NULL;
HrRetCode = spCalculator. CoCreateInstance (CLSID_CALCULATOR );
KG_COM_ASSERT_EXIT (hrRetCode );
HrRetCode = spCalculator-> Add (1, 2, & nSum );
KG_COM_ASSERT_EXIT (hrRetCode );
CComPtr <ICalculator> spCalculator = NULL;
HrRetCode = spCalculator. CoCreateInstance (CLSID_CALCULATOR );
KG_COM_ASSERT_EXIT (hrRetCode );
HrRetCode = spCalculator-> Add (1, 2, & nSum );
KG_COM_ASSERT_EXIT (hrRetCode );
In the above Code, you may suspect that the smart pointer does not meet RAII, but it does. RAII does not require resource management objects to be in the same declared period as resources. It only needs to bind the resource to a resource management object when it is obtained. ATL thinks this is the best way to do this and the code is clear and simple.
Let's take a look at the value assignment operator of CComPtr, which does not perform automatic interface query (it should be noted that CComPtr has added this operation to some value assignment operators in subsequent versions ). ATL considers that with the QueryInterface interface, developers can conveniently complete interface query. As follows:
View plaincopy to clipboardprint? ICalculator * pCalculator = NULL;
CComPtr <IUnknown> spIUnknown = NULL;
HrRetCode = spIUnknown. CoCreateInstance (CLSID_CALCULATOR );
KG_COM_ASSERT_EXIT (hrRetCode );
HrRetCode = spIUnknown. QueryInterface (& pCalculator); // use this function to query the interface
KG_COM_ASSERT_EXIT (hrRetCode );
HrRetCode = pCalculator-> Add (1, 2, & nSum );
KG_COM_ASSERT_EXIT (hrRetCode );
ICalculator * pCalculator = NULL;
CComPtr <IUnknown> spIUnknown = NULL;
HrRetCode = spIUnknown. CoCreateInstance (CLSID_CALCULATOR );
KG_COM_ASSERT_EXIT (hrRetCode );
HrRetCode = spIUnknown. QueryInterface (& pCalculator); // use this function to query the interface
KG_COM_ASSERT_EXIT (hrRetCode );
HrRetCode = pCalculator-> Add (1, 2, & nSum );
KG_COM_ASSERT_EXIT (hrRetCode );
Hmm ~ The problem is solved, which is in line with the ATL philosophy: simple! Efficient! If you want to learn more about the query interface in the overload of the value assignment operator, you can refer to the "Automatic query interface brings convenience and also hides crisis" section 26.
Author's "liuchang5 column"