有时候脑袋短路,加上记忆力退化,很多已知的概念又模糊混淆了。唉,杯具。
今天就在想一个拥有虚函数的基类,它的对象是不是没有vptr和vtable?因为它本身很确定了,如下:
class CA
{
public:
virtual void Func1()
{
printf(“CA::Func1\n”);
}
};
CA a;
printf(“sizeof(a)=%d, sizeof(CA)=%d\n”, sizeof(a), sizeof(CA));
a.Func1();
事实是sizeof均为4,也就是说有vptr和vtable!而a.Func1的调用也确实是很确定的:
00424591 lea ecx,[a]
00424594 call CA::Func1 (41564Ah)
但若使用指针调用则:
CA *pa = &a;
0042D241 lea eax,[a]
0042D244 mov dword ptr [pa],eax
pa->Func1();
0042D247 mov eax,dword ptr [pa]
0042D24A mov edx,dword ptr [eax]
0042D24C mov esi,esp
0042D24E mov ecx,dword ptr [pa]
0042D251 mov eax,dword ptr [edx]
0042D253 call eax
很明显是通过vptr和vtable实现的。想想多态的概念也确实是虚函数+指针/引用的结合实现的。
因此一个类只要拥有虚函数则一定是拥有vptr和vtable的,编译器可以不用区分这是不是多态(指针/引用的对象是基类CA的还是子类CB的)来分别生成代码,如下:
CA *pa = &b;
0042D264 lea eax,[b]
0042D267 mov dword ptr [pa],eax
pa->Func1();
0042D26A mov eax,dword ptr [pa]
0042D26D mov edx,dword ptr [eax]
0042D26F mov esi,esp
0042D271 mov ecx,dword ptr [pa]
0042D274 mov eax,dword ptr [edx]
0042D276 call eax
又在想另一个问题:若一个拥有虚函数的子类继承自一个拥有虚函数的基类,它的对象是不是有两套vptr和vtable?如下
class CB : public CA
{
public:
virtual void Func2()
{
printf(“CB::Func2\n”);
}
};
答案是不需要,因为可以通过一套vptr和vtable调用到各自的成员函数,只有多重继承时才会有多套vptr和vtable!
2012/12/04
虚函数
2012/10/23
ATL碎语CAtlModule CAtlBaseModule
// CAtlModule模块基类
__declspec(selectany) CAtlModule* _pAtlModule = NULL; // 全局变量, 代表一个模块
struct _ATL_MODULE70
{
UINT cbSize;
LONG m_nLockCnt;
_ATL_TERMFUNC_ELEM* m_pTermFuncs;
CComCriticalSection m_csStaticDataInitAndTypeInfo;
};
typedef _ATL_MODULE70 _ATL_MODULE;
class ATL_NO_VTABLE CAtlModule : public _ATL_MODULE
{
CAtlModule() throw()
{
// Should have only one instance of a class
// derived from CAtlModule in a project.
// 一个工程中仅有一个CAtlModule子类的实例
// 其一般也是一个全局变量, 因为它会被赋值给_pAtlModule
ATLASSERT(_pAtlModule == NULL);
// _ATL_MODULE中的变量初始化
cbSize = 0;
m_nLockCnt = 0;
m_pTermFuncs = NULL;
_pAtlModule = this; // 赋值给_pAtlModule
m_pGIT = NULL;
if (FAILED(m_csStaticDataInitAndTypeInfo.Init()))
{
// 注意此if块中返回时, _pAtlModule已被赋值一个失败初始化的对象
ATLTRACE(atlTraceGeneral, 0, _T(“ERROR : Unable to initialize critical section in CAtlModule\n”));
ATLASSERT(0);
CAtlBaseModule::m_bInitFailed = true;
return;
}
// Set cbSize on success.
cbSize = sizeof(_ATL_MODULE);
}
};
// CAtlBaseModule的定义和实现分别在VC\atlmfc\include\atlcore.h和VC\atlmfc\src\atl\atls\atlbase.cpp中
// atlbase.cpp定义了其一个全局变量_AtlBaseModule
#pragma init_seg( lib )
CAtlBaseModule _AtlBaseModule;
extern CAtlWinModule _AtlWinModule = CAtlWinModule();
struct _ATL_BASE_MODULE70
{
UINT cbSize;
HINSTANCE m_hInst;
HINSTANCE m_hInstResource;
bool m_bNT5orWin98;
DWORD dwAtlBuildVer;
const GUID* pguidVer;
CComCriticalSection m_csResource;
CSimpleArray<HINSTANCE> m_rgResourceInstance;
};
typedef _ATL_BASE_MODULE70 _ATL_BASE_MODULE;
class CAtlBaseModule : public _ATL_BASE_MODULE
{
CAtlBaseModule::CAtlBaseModule() throw()
{
cbSize = sizeof(_ATL_BASE_MODULE);
m_hInst = m_hInstResource = reinterpret_cast<HINSTANCE>(&__ImageBase);
m_bNT5orWin98 = false;
OSVERSIONINFO version;
memset(&version, 0, sizeof(version));
version.dwOSVersionInfoSize = sizeof(version);
::GetVersionEx(&version);
if(version.dwPlatformId == VER_PLATFORM_WIN32_NT)
{
if(version.dwMajorVersion >= 5)
{
m_bNT5orWin98 = true;
}
}
else if(version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
{
if((version.dwMajorVersion > 4) || ((version.dwMajorVersion == 4) &&
(version.dwMinorVersion > 0)))
{
m_bNT5orWin98 = true;
}
}
dwAtlBuildVer = _ATL_VER;
pguidVer = &GUID_ATLVer70;
if (FAILED(m_csResource.Init()))
{
ATLTRACE(atlTraceGeneral, 0, _T(“ERROR : Unable to initialize critical section in CAtlBaseModule\n”));
ATLASSERT(0);
CAtlBaseModule::m_bInitFailed = true;
}
}
}
2012/02/18
获取系统调用号
ULONG GetSyscallId(char *pszSyscallName)
{
if(NULL == pszSyscallName || '\0' == pszSyscallName[0])
{
return 0;
}
HMODULE hMod = GetModuleHandle(_T("ntdll.dll"));
if(NULL == hMod)
{
return 0;
}
unsigned char *pByte = (unsigned char *)GetProcAddress(hMod, pszSyscallName);
if(NULL == pByte)
{
return 0;
}
if(0xB8 == pByte[0] && 0xBA == pByte[5])
{
return *(ULONG *)(pByte + 1);
}
else
{
return 0;
}
}
这个方法的原理很早就知道了,系统调用时会用eax保存系统调用号:
0: kd> u ntdll!NtCreateProcessEx
ntdll!NtCreateProcessEx:
7c92d15e b830000000 mov eax,30h
7c92d163 ba0003fe7f mov edx,offset SharedUserData!SystemCallStub (7ffe0300)
7c92d168 ff12 call dword ptr [edx]
7c92d16a c22400 ret 24h
7c92d16d 90 nop
一直没实现,今天实现了一下。
实现过程中遇到了一个char比较的问题,上面的代码中一开始pByte我使用的是char*,结果进行char比较时发现char为0xB8和0xBA时却与它们不相等。看了汇编代码才知道是将char movsx符号扩展后再进行dwrod的cmp了。用unsigned char*即可解决问题。编译器应该是因对齐进行了优化。
2011/12/24
天书夜读2.2思考与练习
int a, b, d;
d = a + b;
int i = 1, c = 0;
while(c < 0x64)
{
c += i;
}
switch(c)
{
case 0:
d = 1;
case 1:
d = c;
break;
default:
d = 0;
}
return d;
2011/10/13
__declspec(novtable)
novtable:
可用于任何类声明中,但最好只用于纯接口类,即类本身从不实例化。此关键字的声明将阻止编译器对构造和析构函数的vfptr的初始化。可优化编译后代码大小。
如果试图实例化一个用__declspec(novtable)声明的类然后访问类中成员,则会在运行时产生访问错误(access violation,即AV)。