libatbus的几个藏得很深的bug
在写这篇文章前,我突然想到以前流行了一段时间的服务器面试题:当一个BUG只有几百万分之一的概率会出现,怎么办?这个问题在这个BUG里只是毛毛雨而已,因为这次的BUG的出现概率是夸张的三亿分之一。
在写这篇文章前,我突然想到以前流行了一段时间的服务器面试题:当一个BUG只有几百万分之一的概率会出现,怎么办?这个问题在这个BUG里只是毛毛雨而已,因为这次的BUG的出现概率是夸张的三亿分之一。
记一下最近踩得两个C++独有的暗坑,其中一个和ABI相关。第二个坑其实之前研究过,但是没有实例,这次算是碰到了个典型的实例。
在项目中碰到的实例的大致流程是:
前几天我们项目的日志系统出现了一点问题,但是一直没有时间去深究。 昨天在同事的帮助下,无意中猜了一种可能性,结果还真被我猜中了,于是今天就特别研究了一下,记录下来。
终于到最后的虚继承了。
测试代码如下:
#include <ctime>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <stdint.h>
#include <cstddef>
#include <string>
class foo_a
{
public:
virtual ~foo_a(){}
virtual void info() {
printf("%s:%d\n", __FUNCTION__, __LINE__);
}
void print(){
printf("%s:%d\n", __FUNCTION__, __LINE__);
}
};
class foo_l
{
public:
int l;
foo_l(): l(1){}
virtual void info() {
printf("%s:%d\n", __FUNCTION__, __LINE__);
}
};
class foo_r: virtual public foo_a
{
public:
int r;
foo_r(): r(2){}
};
class foo_c: virtual public foo_l, public foo_r
{
public:
virtual void info() {
printf("%d,%d. %s:%d\n", l, r, __FUNCTION__, __LINE__);
}
};
int main(int argc, char* argv[]) {
void (foo_a::*vptr1)() = &foo_a::info;
void (foo_l::*vptr2)() = &foo_l::info;
void (foo_a::*ptr)() = &foo_a::print;
foo_c c;
foo_r r;
foo_a a;
printf("pword size = %d\n", (int)(sizeof(size_t)));
printf("address of &foo_a::info = 0x%llxH\n", &foo_a::info);
printf("pointer to &foo_a::info = 0x%llxH\n", vptr1);
printf("sizeof vptr = %d\n", (int)(sizeof(vptr1)));
(a.*vptr1)();
(r.*vptr1)();
(c.*vptr1)();
(c.*vptr2)();
c.info();
(c.*ptr)();
return 0;
}
因为是兼容虚继承和非虚继承的,所以赋值的部分的汇编是一样的。这里就不贴了。关键在于执行期它是怎么找到虚基类的。请往下看:
接下来是多重继承,用到的测试代码如下所示:
#include <ctime>
#include <vector>
#include <algorithm>
#include <cstdio>
#include <stdint.h>
#include <cstddef>
#include <string>
class foo_a
{
public:
virtual ~foo_a(){}
virtual void info() {
printf("%s:%d\n", __FUNCTION__, __LINE__);
}
};
class foo_b
{
public:
virtual ~foo_b(){}
// 这个函数用于试foo_b的结构与foo_a不同
// 以防止VC对foo_a和foo_b的`vcall'{8}'的代码合并
virtual void inspect() {
printf("%s:%d\n", __FUNCTION__, __LINE__);
}
virtual void info() {
printf("%s:%d\n", __FUNCTION__, __LINE__);
}
void print() {
printf("%s:%d\n", __FUNCTION__, __LINE__);
}
};
class foo_c: public foo_a, public foo_b
{
public:
virtual void info() {
printf("%s:%d\n", __FUNCTION__, __LINE__);
}
};
int main(int argc, char* argv[]) {
void (foo_a::*vptr1)() = &foo_a::info;
void (foo_b::*vptr2)() = &foo_b::info;
void (foo_b::*ptr)() = &foo_b::print;
foo_c c;
foo_b b;
foo_a a;
printf("word size = %d\n", (int)(sizeof(size_t)));
printf("address of &foo_a::info = 0x%llxH\n", &foo_a::info);
printf("pointer to &foo_a::info = 0x%llxH\n", vptr1);
printf("address of &foo_b::info = 0x%llxH\n", &foo_b::info);
printf("pointer to &foo_b::info = 0x%llxH\n", vptr2);
printf("sizeof vptr = %d\n", (int)(sizeof(vptr1)));
(a.*vptr1)();
(b.*vptr2)();
(c.*vptr1)();
(c.*vptr2)();
(c.*ptr)();
return 0;
}
最近在《C++对象模型》一书里说到的virtual的成员函数指针,低于128的被cfront编译器认为是虚表偏移量(支持子类对父类函数的覆盖)。VC只是提了下单继承、多继承和虚继承的实现方案不同,GCC没有提及,所以就专门稍微深入分析研究下他们的实现机制。