3. Implementation/COM & ActiveX

ATL Smart Pointer Class (스마트 포인터 클래스)

SSKK 2008. 10. 3. 12:15

ATL에서 Visual C++_com_ptr_t 스마트 포인터 템플릿 클래스에 해당되는 클래스로 CComPtrCComQIPtr 템플릿 클래스를 제공.

 

l  자동적으로 AddRefRelease 호출

l  인터페이스 포인터 변수와 이들 클래스의 인스턴스를 구별없이 사용

l  CComQIPtr은 추가로 QueryInterface를 통하여 자동적으로 인터페이스를 질의하는 기능 제공

l  _com_ptr_t와는 달리 COM 객체의 인스턴스를 생성하는 일은 하지 못함

 

1.    CComPtr

 

다음의 생성자를 가짐

template < class T >

class CComPtr

 

CComPtr::CComPtr();

CComPtr::CComPtr(T * lp);

CComPtr::CComPtr(const CComPtr<T>& lp);

 

사용 예

void Foo(IUnknown * p)

{

HRSULT hr;

 

// 생성자 사용 예

CComPtr<IUnknown> pUnk1;

CComPtr<IUnknown> pUnk2(p);

CComPtr<IUnknown> pUnk3(pUnk2);

 

// ->& 연산자 사용 예

CComPtr<IHello> pHello1;

hr = pUnk->QueryInterface(IID_IHello, (LPVOID*)&pHello1);

if(FAILED(hr)) return;

 

// 대입 연산자 사용 예

IHello * pHello;

CComPtr<IHello> pHello2;

 

pHello2 = pHello1;

 

CComPtr<IHello> pHello3;

hr = p->QueryInterface(IID_IHello, (LPVOID*)&pHello;

if(FAILED(hr) return;

 

CComPtr<IHello> pHello4(pHello3);

pHello4 = pHello1;

pHello4 = pHello;

}

 

사용시 주의해야 할 점

n  &연산자를 사용할 때는 해당 CComPtr 클래스 객체의 멤버 포인터 p가 반드시 NULL이어야 한다.

n  -> 연산자를 사용할 때는 해당 CComPtr 클래스 객체의 멤버 포인터가 반드시 NULL이 아니어야 한다.

n  -> 연산자를 사용할 때 Release 메서드를 호출하는 경우

 

pUnk->Relase();

 

// 위 코드는 실제로는 다음 코드와 같다.

 

pUnk.p->Release();

 

이 경우 CComPtr 클래스의 소멸자에서 다시 한번 Release 메서드가 호출되므로 에러가 된다.

 

Release 메서드를 명시적으로 호출하고 한다면 -> 연산자 대신에 반드시 점(.) 연산자를 사용해야 한다.

 

pUnk.Release();

 

위 코드의 경우 CComPtr 클래스의 Release 멤버함수에서 멤버 포인터 p Release 메서드를 호출하고 NULL값을 지정하므로 소멸자에서 두번 Release 메서드를 호출하지 않게 된다.

n  CComPtr 템플릿 클래스는 서로 다른 인터페이스 포인터나 인터페이스 포인터를 캡슐화 하는 CComPtr 클래스 객체를 수용하지 못한다.

 

CComPtr<IUnknown> pUnk;

CComPtr<IHello> pIHello;

// 에러!!! ? 인터페이스가 다름.

CComPtr<IGoodbye> pIGoodbye(pUnk);

pIHello = pUnk;

 

CComPtr의 이러한 문제점을 보완하기 위해 CComQIPtr 템플릿 클래스를 제공

 

 

2.    CComQIPtr

 

CComQIPtr 클래스는 템플릿 매개변수로 인터페이스에 대한 GUID 포인터를 추가로 받아들이는 것을 제외하고는 CComPtr 클래스와 유사하다.

 

CComQIPtr 클래스는 IUnknown 인터페이스 포인터를 매개변수로 받아들이는 생성자와 대입 연산자 멤버 함수를 제공함으로써, 다른 인터페이스 포인터나 다른 인터페이스 포인터를 캡슐화하는 CComPtr 또는 CComQIPtr 클래스 객체가 매개 변수로 넘어올 때 QueryInterface 메서드를 호출해준다.

 

CComQIPtr::CComQIPtr(IUnknown * lp);

T* CComQIPtr::operator=(IUnknown*lp);

 

CComQIPtr 클래스의 사용 예

 

void Foo(IUnknown * p)

{

CComQIPtr<IHello, &IID_IHello> pIHello(p);

CComQIPtr<IGoodbye, &IID_IGoodbye> pIGoodbye1(pIHello);

CComQIPtr<IGoodbye, &IID_IGoodbye> pIGoodbye2;

pIGoodbye2 = pIHello;

 

CComQIPtr<IGoodbye, &IID_IGoodbye> pIGoodbye3(pIGoodbye1);

CComQIPtr<IGoodbye, &IID_IGoodbye> pIGoodbye4;

pIGoodbye4 = pIGoodbye3; // Addref 메서드만 호출

}

 

CComPtr 템플릿 클래스는 이제 필요없게 되는 걸까?

n  ATL 버전과의 호환성 때문이라도 남아 있어야 한다

n  CComQIPtr 템플릿 클래스가 IUnknown 인터페이스는 수용하지 않는다

 

CComQIPtr<IUnknown, &IID_IUnknown> pUnk;

 

위 코드에 대해서 컴파일러는 컴파일 에러 메시지를 보여주게 된다. 따라서, IUnknown 인터페이스를 수용하는 템플릿 클래스 객체를 사용하기 위해서는 다음과 같이 CComPtr 템플릿 클래스를 사용해야 한다.

 

CComPtr<IUnknown> pUnk;

 

출처 : 전병선의 Component Development with Visual C++ & ATL