目录
奇怪的问题出现了,第四个调用fun函数居然调用出错了,我明明调用的是fun,怎么最后调用了A1B的b呢?
记录Socket多线程创建读数据线程时遇到的问题
问题:
linux使用pthread_create创建线程时,将多继承的子类传入一个静态函数,导致子类函数调用失败。
类定义:
内容说明:A与A1fuck()区别,A1为虚函数, class AB继承A B, class A1B继承A1 B,注意继承顺序为AB :public B, public A; A1B : public B, public A1;
class A
{
public:
virtual ~A(){}
public:
void fun()
{
cout << "base A fun" << endl;
}
static void* entry(void *arg)
{
A* tmp = (A*)arg;
tmp->fun();
return 0;
}
};
class A1
{
public:
virtual ~A1(){}
public:
virtual void fun()
{
cout << "base A1 fun" << endl;
}
static void* entry(void *arg)
{
A1* tmp = (A1*)arg;
tmp->fun();
return 0;
}
};
class B
{
public:
virtual ~B(){}
public:
virtual void b() = 0;
};
class AB : public B, public A
{
public:
virtual ~AB(){}
public:
void b() override
{
cout << "AB b" << endl;
}
public:
void fun() { cout << "AB fun";}
};
class A1B : public B, public A1
{
public:
~A1B(){}
public:
void b() override
{
cout << "AB b" << endl;
}
public:
virtual void fun() { cout << "AB fun" << endl;}
};
主函数调用:
AB ab;
A1B a1b;
pthread_t fd;
pthread_create(&fd, nullptr, &A::entry, &ab); /* 1.非虚函数A::fun() */
A* aba = (A*) &ab;
aba->fun(); /* 2.非虚函数A::fun() */
A1* a1ba = (A1*) &a1b;
a1ba->fun(); /* 3.虚函数A1B::fun() */
pthread_create(&fd, nullptr, &A1::entry, &a1b); /* 4.出现异常,虚函数A1B::b() */
奇怪的问题出现了,第四个调用fun函数居然调用出错了,我明明调用的是fun,怎么最后调用了A1B的b呢?
将继承顺序替换A1B : public A1, public B 调用到了正常的A1B::fun() !!!
问题分析:
AB::fun()为非虚函数,不管怎么调用都为A::fun();
而A1B::fun()为虚函数,A1B*转成A1*之后调用fun函数由于A1B重写了fun()的原因正常是要调用A1B::fun(),而4却调用了A1B::b(),改变继承顺序成功调用了A1B::fun();
虚函数导致?
多继承机制?
内存分配?
虚函数表的多继承问题?
这里假设多继承中虚函数表的的存放是根据继承顺序一个一个排下去的。
为了确认问题,我在A1新增了一个虚函数c(),同时在A1B重写了c(),entry调用c。重新运行下面的代码
pthread_create(&fd, nullptr, &A1::entry, &a1b); /* 4.出现异常,虚函数A1B::fun */
调用了fun()!!! 新增c后虚函数表的排列顺序为b fun c。调用第二个子类的第二个虚函数c,结果调用了整个虚函数表的第二个虚函数fun。而没新增前虚函数表的排列顺序为b fun。调用第二个子类的第一个虚函数fun,结果调用了整个虚函数表的第一个虚函数b。与实际符合。
pthread_create传入的第四个参数为void*,比对一下:
cout << (void*) &a1b << endl; /* 0x7fffbc6a8890 */
cout << (A1*) &a1b << endl; /* 0x7fffbc6a8898 */
void*传入的是子类地址,而A1*传入的是父类A1*的地址。由于传入的地址不同,所以才会出现pthread_create调用函数出现问题的情况。
结论:
多继承中虚函数表根据继承顺序排列虚函数。
pthread_create传入的第四个参数为void* 如果不进行强制类型转换,传入的地址为子类地址,而强制类型转换会使得传入的地址发生偏移,让后面再转成子类A1*不会出现问题。
最终需要在将void*强制转成(A1*),调用的函数为子类的fun函数,即 :
pthread_create(&fd, nullptr, &A1::entry, (A1*)&a1b);