==> Object.h <==
#include <stdio.h>
class Object {
public:
Object();
#ifdef TEST
void NoneVirtualFunc() {
printf("%s, size of class:%lu\n", __PRETTY_FUNCTION__, sizeof(Object));
}
#endif
#ifdef TEST
virtual void Func2();
virtual void Func1();
#else
virtual void Func1();
virtual void Func2();
#endif
virtual void Access();
#ifdef TEST
void SetOther(int other) {
m_other = other;
}
virtual void Other() {
printf("%s\n", __PRETTY_FUNCTION__);
}
#endif
private:
int m_i1;
#ifdef TEST
int m_other;
#endif
int m_i2;
};
==> Object.cpp <==
#include "Object.h"
Object::Object(): m_i1(1), m_i2(2) {
printf("%s, constructor init, i1:%d, i2:%d\n", __PRETTY_FUNCTION__, m_i1, m_i2);
}
void Object::Func1() {
printf("%s\n", __PRETTY_FUNCTION__);
}
void Object::Func2() {
printf("%s\n", __PRETTY_FUNCTION__);
}
void Object::Access() {
printf("%s, size of class:%lu, i1:%d, i2:%d\n", __PRETTY_FUNCTION__, sizeof(Object), m_i1, m_i2);
}
==> test.cpp <==
#include "Object.h"
//for more details about vtable, see: http://blog.csdn.net/haoel/article/details/1948051
int main(int argc, char* argv[]) {
Object* obj = new Object();
obj->Func1();
obj->Func2();
#ifdef TEST
obj->SetOther(1000);
#endif
obj->Access();
#ifdef TEST
obj->NoneVirtualFunc();
obj->Other(); //segmentation fault
#endif
return 0;
}
==> Makefile <==
all:
@g++ -g -fPIC -o Object.o -fno-rtti -c Object.cpp
@g++ -shared -o libObject.so Object.o
@g++ -g -Wall test.cpp -o test -L. -lObject -Wl,-rpath=.
@./test
@rm -f test *.o *.so core*
test:
@g++ -g -fPIC -o Object.o -fno-rtti -c Object.cpp
@g++ -shared -o libObject.so Object.o
@g++ -g -Wall test.cpp -o test -L. -lObject -Wl,-rpath=. -DTEST
@./test
@rm -f test *.o *.so core*
==> RESULT <==
$ make
Object::Object(), constructor init, i1:1, i2:2
virtual void Object::Func1()
virtual void Object::Func2()
virtual void Object::Access(), size of class:16, i1:1, i2:2
$ make test
Object::Object(), constructor init, i1:1, i2:2
virtual void Object::Func2()
virtual void Object::Func1()
virtual void Object::Access(), size of class:16, i1:1, i2:1000
void Object::NoneVirtualFunc(), size of class:24
make: *** [test] Segmentation fault (core dumped)
make: *** Deleting file `test'
==> KNOWLEDGE <==
一个类一旦已经编译成so了,那么虚函数以及成员变量就都确定了,也就是影响一个类的内存映像的东西都是确定的了。
所以,修改头文件时需要遵循以下原则:
1。可以随意添加非虚函数;
2。不要修改虚函数的相对位置,更不要添加新的虚函数;
3。最好不要添加新的成员变量,如果添加也要在所有成员变量的后面添加,不过如果你不清楚so里的用法的话,很有可能出问题;
其实,说到本质上,就是要心里很清楚,修改前和修改后,该类的内存映像到底是怎样的,固化在so里的代码是不会再变化了;
有兴趣的,可以debug看下虚函数表里指针的情况;如果不用-fno-rtti参数,那么vtable里会多出两项用于typeinfo相关的内容;
增加了新的虚函数后,通过nm -C test | grep Object可以看到,生成的可执行程序里是不会有Other这个虚函数的:
00000000004008f8 W Object::NoneVirtualFunc()
0000000000400920 W Object::SetOther(int)
U Object::Object()
0000000000400a60 r Object::NoneVirtualFunc()::__PRETTY_FUNCTION__
而且新加的函数都是W类型,下面通过另一个例子来验证W类型:
==> Object.h <==
#include <stdio.h>
class Object {
public:
void func() {
printf("It's the implementation of %s in header file\n", __PRETTY_FUNCTION__);
}
};
==> test.cpp <==
#include <stdio.h>
#include "Object.h"
int main(int argc, char* argv[]) {
Object obj;
obj.func();
return 0;
}
==> RESULT <==
$ g++ -o test test.cpp
$ ./test
It's the implementation of void Object::func() in header file
==> Object.cpp <==
#include <stdio.h>
class Object {
public:
void func();
};
void Object::func() {
printf("%s\n", __PRETTY_FUNCTION__);
}
==> RESULT <==
$ g++ -c -o Object.o Object.cpp
$ g++ -o test test.cpp Object.o
$ ./test
void Object::func()