写麻将客户端时,遇到这么一个问题:需要将一个String类型转换为byte类型,然后通过广播发送出去。
Java的实现方法如下:
String s = "test";
byte b[] = s.getByte();//字符串转byte
s = new String(b);//byte转字符串
由这个问题,联想到,C++中的类在内存中是如何存储的呢,于是开始动手写代码测试,代码如下:
1.
class class_a
{
public:
int a;
class_a()
{
a=1;
}
};
该实例占4字节 说明实例中不包含构造函数的地址
2.
class class_b
{
public:
int a;
class_b(){};
private:
void fun_1(){};
};
class class_c
{
public:
int a;
class_c(){};
void fun_1(){};
};
两个类的实例都是占4字节 说明不管是public类型还是private类型的成员函数,其地址均不包含在实例内
3.
class class_d
{
public:
int a;
virtual void fun_1(){};
};
class class_d
{
public:
int a;
virtual void fun_1(){};
virtual void fun_2(){};
};
这两个类的实例占8字节,而且,不管类中的虚函数个数为多少,均占8个字节。
这个现象就证实了“虚表”这个概念:一个类的虚函数的地址是保存在一个虚表中的,而虚表的表头则存储在类的每个实例中。
下面来验证多重继承时,实例中虚表的个数(比如,是不是如果一个类继承了另一个类,虚表的个数就变成了2个呢?),测试代码和输出如下:(继承关系为:class_2、class_3继承class_1,class_4继承class_3,class_5继承class_2和class_3)
//有虚函数的继承
class class_1//实例占8字节
{
public:
int a;
virtual void fun_1(){};
class_1()
{
a = 1;
printf("class_%d 实例化\r\n",a);
}
};
class class_2 : class_1//实例占12字节
{
public:
int a;
void sub_fun1(){};
class_2()
{
a=2;
printf("class_%d 实例化\r\n",a);
}
};
class class_3 : class_1//实例占12字节
{
public:
int a;
virtual void fun_1(){};
class_3()
{
a=3;
printf("class_%d 实例化\r\n",a);
}
};
class class_4 : class_3//实例占16字节
{
public:
int a;
virtual void sub_fun_1(){};
class_4()
{
a = 4;
printf("class_%d 实例化\r\n",a);
}
};
class class_5: class_3,class_2
{
public:
int a;
virtual void sub_fun_1(){};
class_5()
{
a = 5;
printf("class_%d 实例化\r\n",a);
}
};
//没有虚函数的继承
class class_6
{
public:
int a;
class_6()
{
a=6;
printf("class_%d 实例化\r\n",a);
}
};//实例占四个字节
class class_7:class_6
{
public:
int a;
class_7()
{
a=7;
printf("class_%d 实例化\r\n",a);
};
};//实例占八个字节
int _tmain(int argc, _TCHAR* argv[])
{
int *i;
class_1 *p1 = new class_1();
int t1 = sizeof(*p1);
i=(int*)p1;
printf("size %d content of p1: %d %d \r\n",t1,i[0],i[1]);
class_2 *p2 = new class_2();
int t2 = sizeof(*p2);
i=(int*)p2;
printf("size %d content of p2: %d %d %d \r\n",t2,i[0],i[1],i[2]);
class_3 *p3 = new class_3();
int t3 = sizeof(*p3);
i=(int*)p3;
printf("size %d content of p3: %d %d %d \r\n",t3,i[0],i[1],i[2]);
class_4 *p4 = new class_4();
int t4 = sizeof(*p4);
i=(int*)p4;
printf("size %d content of p4: %d %d %d %d \r\n",t4,i[0],i[1],i[2],i[3]);
class_5 *p5 = new class_5();
int t5 = sizeof(*p5);
i=(int*)p5;
printf("size %d content of p5: %d %d %d %d %d %d %d \r\n",t5,i[0],i[1],i[2],i[3],i[4],i[5],i[6]);
class_6 *p6 = new class_6();
int t6 = sizeof(*p6);
i=(int*)p6;
printf("size %d content of p6: %d\r\n",t6,i[0]);
class_7 *p7 = new class_7();
int t7 = sizeof(*p7);
i=(int*)p7;
printf("size %d content of p7: %d %d\r\n",t7,i[0],i[1]);
system("pause");
return 0;
}
结果分析:
可以看到,在class_5的实例化过程中,class_1实例化了两次。
从这个结果中我们便可以看出虚基类的用处是什么了。此时没有用虚基类,如果我们要调用class_1实例中的fun_1,编译器便不能确定我们是要调用哪个实例的成员,然后会报错——“fun_1”不明确。如图
当然,如果在class_5中重写虚函数,则不会报错。
参考:
RTTI、虚函数和虚基类的实现方式、开销分析及使用指导 http://www.baiy.cn/doc/cpp/inside_rtti.htm
求一个类的sizeof应该考虑的问题 http://blog.sina.com.cn/s/blog_728161840100u2ib.html