关于类成员函数
- 类的大小本身不会包含类成员函数的指针地址,
class TestASize {
int id = 444;
};
class TestBSize {
public:
int id = 232;
void test1() {}
void test2() {}
};
int main()
{
TestBSize b;
TestASize a;
std::cout << "a size: " << sizeof(a) << std::endl;
std::cout << "b size: " << sizeof(b) << std::endl;
std::cout << "TestASize: " << sizeof(TestASize) << std::endl;
std::cout << "TestBSize: " << sizeof(TestBSize) << std::endl;
}
打印结果为: a size: 4 b size: 4 TestASize: 4 TestBSize: 4
- 成员函数是独立保存的,多个对象可以共用(在win里,甚至相同进程,也是一样的)
成员函数的指针地址不能通过std::cout打印,因为没有针对(__thiscall Class:: *)()类型重载operator<< 但可以通过printf, 使用%p打印
printf("TestBSize::test1: %p\n", &TestBSize::test1);
printf("TestBSize::test2: %p\n", &TestBSize::test2);
打印结果: TestBSize::test1: 00E2143D TestBSize::test2: 00E2144C
成员虚函数
- 当存在类中存在虚函数时,就会生成虚函数表
- 虚函数表是共用的,每个对象都会保存其指向虚函数的指针
#include <iostream>
class A {
public:
virtual void test() {
std::cout << "A::test" << std::endl;
}
int ai = 0;
};
class B : public A{
public:
void test() override {
std::cout << "B::test" << std::endl;
}
int bi = 1212;
};
int main()
{
A a, a2;
B b, b2;
printf("a size: %i, \n b size: %i\
\n", sizeof(a), sizeof(b));
}
打印结果为: a size: 8, b size: 12
a的大小= vptr +int = 8 b的大小 = vptr + int + int = 12
- 虚表一般保存在对象首位
可以使用如下方式获取
#define GET_VTABLE(c) (int*)(*(int*)(&(c)))
获得虚表后,如果我们甚至可以直接遍历里面的方法
typedef void (*FUNC)();
void PrintVTable(int* vtable) {
std::cout << " vptr: " << vtable;
for (int i = 0; vtable[i] != 0; ++i) {
printf("\n%i, virtual fun addr %p \n", i, vtable[i]);
FUNC f = (FUNC)vtable[i];
f();
}
std::cout << std::endl;
}
int main()
{
A a, a2;
B b, b2;
PrintVTable(GET_VTABLE(a));
PrintVTable(GET_VTABLE(a2));
PrintVTable(GET_VTABLE(b));
PrintVTable(GET_VTABLE(b2));
}
vptr: 00349B34 0, virtual fun addr 0034103C A::test vptr: 00349B34 0, virtual fun addr 0034103C A::test vptr: 00349B4C 0, virtual fun addr 0034114A B::test vptr: 00349B4C 0, virtual fun addr 0034114A B::test
进程间共享内存的问题
- 对于存在虚函数的类,由于存在虚函数表,表是与进程相关的,故每个进程里的虚表地址不一定是一样的
有两个进程,他们都有如下数据
class TestSharedData {
public:
virtual void test_print() {
std::cout << "TestSharedData::test_print: " << i;
}
int i = 23232;
};
class TestSharedDataB : public TestSharedData {
public:
void test_print() override {
std::cout << "TestSharedDataB::test_print : " << i;
}
};
进程a跑如下方法
static void test_shared_a() {
char szBuffer[] = "";
const char* test_str = "This is a shared memory test!!!";
HANDLE hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE,
NULL,
PAGE_READWRITE,
0,
BUFF_SIZE,
shared_mem_name
);
LPVOID lpBase = MapViewOfFile(
hMapFile,
FILE_MAP_ALL_ACCESS,
0,
0,
BUFF_SIZE
);
EDataChunk::instance().init((char*)lpBase, BUFF_SIZE);
{
char* test_ptr = EDataChunk::instance().allocate(sizeof(TestSharedDataB));
TestSharedDataB* b = new(test_ptr) TestSharedDataB();
PrintVTable(GET_VTABLE(*b));
b->test_print();
int temp = 232;
std::cin >> temp;
b->~TestSharedDataB();
EDataChunk::instance().deallocate(test_ptr);
}
EDataChunk::instance().uninit();
UnmapViewOfFile(lpBase);
CloseHandle(hMapFile);
}
进程b跑如下函数
static void test_shared_b() {
TestSharedDataB b;
PrintVTable(GET_VTABLE(b));
HANDLE hMapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS, NULL, shared_mem_name);
if (hMapFile)
{
LPVOID lpBase = MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, 0);
TestSharedDataB* a = (TestSharedDataB*)lpBase;
PrintVTable(GET_VTABLE(*a));
UnmapViewOfFile(lpBase);
CloseHandle(hMapFile);
}
}
a 进程下有如打印: vptr: 0146ABF4 virtual fun addr 013F4F91 data vtable addr:0146ABF4 TestSharedDataB::test_print: 23232
b 会报错,但也会有如下打印: vptr: 00B79BA0 virtual fun addr 00B714B5 data vtable addr:0146ABF4
可以看出,a,b 进程下,TestSharedDataB对象的虚函数表指向是不一致,所以是不能在进程间直接使用同一个对象
- 但对于没有虚函数成员对象,共享内存间是可以直接取用的,因为成员函数地址在编译时已经确定,不需要额外保存指向虚函数表的数据,即使他们函数地址在各进程里不一样。
|