MFC

novtable 사용의미

디버그정 2008. 8. 30. 21:19
virtual 함수를 가질수 없는 클래스
조회(266)
모바일/Embed/WinCE | 2004/08/14 (토) 01:52
추천하기 | 스크랩하기
MGI 파일을 액세스 하기 위해서 파일 헤더를 구조체로 만들었습니다.


typedef struct tagMGI
{
     char m_ID[32];
     WORD m_Version;
     char m_CopyRight[32];
     WORD m_PicCount1;
     WORD m_PicCount2;
     WORD m_Speed;
     WORD m_Reserved1;
     WORD m_ColorCount;
     WORD m_Reserved2;
} MGI;

CRect나 CPoint가 RECT와 POINT로부터 상속받아 편리하게
사용하는 것처럼 저도 이 구조체로부터 상속 받아 클래스를
만들었습니다. 한데, 이 클래스가 virtual 메서드를 갖게되면
클래스를 가리키는 메모리 위치 맨 앞에 vfptr이 붙게됩니다.
그러니까 포인터 크기만큼 다른 데이타들이 밀리게 되는 거죠.
그러므로, virtual 함수를 갖게되면 클래스가 제대로 동작할
수가 없게 됩니다. 원구님 말씀에 따르면, VC가 아닌 다른
컴파일러에는 클래스 메모리 위치 뒤에 vfptr이 붙게 된다더군요.
그렇게 하더라도 이 경우는 아니지만 멤버 변수가 하나도
없는 경우에 sizeof(클래스) == sizeof(구조체)가 되어야
클래스를 사용하는 사람이 편리하게 코딩할 수 있을 것입니다.
아래는 그 클래스의 소스입니다.


class _declspec(novtable) CMGI : public MGI
{
public:
     CMGI()
     {
          memset(this, NULL, sizeof(MGI));
          strcpy(m_ID, "MobileTop's Animation File V1.0");
          m_Version = 100;
          strcpy(m_CopyRight, "MobileTop");
          m_Speed = 2;
          m_ColorCount = 256;
          m_pData = NULL;
          m_BmpSize = 0;
     };
     CMGI(LPCTSTR pFileName)
     {
          DWORD     dwBytesRead = 0;
          HANDLE hFile = ::CreateFile(pFileName,
                                    GENERIC_READ,
                                    FILE_SHARE_READ,
                                    NULL,
                                    OPEN_EXISTING,
                                    FILE_ATTRIBUTE_NORMAL,
                                    NULL);
          ASSERT(INVALID_HANDLE_VALUE != hFile);
          // 헤더 읽기
          BOOL bRead = ::ReadFile(hFile, this, sizeof(MGI), &dwBytesRead, NULL);
          int size = ::GetFileSize(hFile, NULL);
          size -= sizeof(MGI);
          m_pData = new BYTE[size];
          bRead = ::ReadFile(hFile, m_pData, size, &dwBytesRead, NULL);
          ::CloseHandle(hFile);
          m_BmpSize = size/m_PicCount2;
     };
     ~CMGI()
     {
          if(m_pData)
               delete m_pData;
     };
// properties...
public:
     BYTE *m_pData;
     DWORD m_BmpSize;
};


특이한 점이 있다면 클래스 정의 부분에 _declspec(novtable)를
넣었다는 것입니다. 이 부분은 컴파일러 스위치로서 '이 클래스는
가상 테이블(vtable: vfptr이 가리키는 테이블)을 가질수 없다'는
뜻입니다. MSDN에는 다음과 같이 나와있습니다.

This form of _declspec can be applied to any class declaration, but should only be applied to pure interface classes, that is classes that will never be instantiated on their own. The _declspec stops the compiler from generating code to initialize the vfptr in the constructor(s) and destructor of the class. In many cases, this removes the only references to the vtable that are associated with the class and, thus, the linker will remove it. Using this form of _declspec can result in a significant reduction in code size.

이 _declspec 용례는 어떤 클래스 선언에도 적용할 수 있다. 허나 순수 인터페이스 클래스에서만 적용해야 한다. 이것은 스스로 인스턴스가 만들어지지 않음을 뜻한다. 이런 구문은 컴파일러가 이 클래스의 생성자와 파괴자에서 vfptr을 초기화하는 코드를 만들어내지 않도록 한다. 대부분의 경우에 이것은 클래스에 연결된 vtable에 대한 참조만 제거한다. 그러므로, 링커는 그걸 제거한다. 이렇게 사용하면 코드 크기를 많이 줄일 수 있다.