本博文是在阅读android源码的过程中,发现代码中有类继承结构体的情况,之前很少见,就在这里总结一下,加深一下印象。本博文只是本人学习笔记记录所得,如果博文中有什么问题,请大家指出来,大家一起学习。
1.C++中的结构体
C++中的结构体和类基本上没什么区别了,基本上可以把它当做类来使用,在下面这个实验例程验证了C++结构体和类很多共通性质:包括多态,虚指针,继承,内存分布。所以看到类继承于某个结构体也不要奇怪了。例程,我在本地编译验证过,伙计们可以亲自下刀试试。
1)源代码
#include<iostream>
using namespace std;
struct test1{
int pub; //C++结构体默认属性是public,在后面main函数中可直接访问
public:
int a;
int b;
test1(){ //也可以有构造函数。
cout <<"test1"<<endl;
}
int fun1(int,int);
int fun2(int,int);
virtual int fun3(int,int); //也可以有虚函数
private:
int aa; //private 真的是私有吗
};
int test1::fun1(int a,int b){
cout<<"hello shanghai\n"<<endl;
return 0;
}
int test1::fun2(int a,int b){
cout<<"hello beijing \n"<<endl;
return 0;
}
int test1::fun3(int a,int b){
cout<<"hello shenzhen\n"<<endl;
return 0;
}
struct test2:test1{ //test2继承于test1,它的内存分布和test1有什么区别吗
test2(){
cout<<"test2"<<endl;
}
int c;
int d;
int fun3(int a,int b){
cout<<"hello hangzhou\n"<<endl;
return 0;
}
};
int main(){
test1 t1;
test2 t2;
t2.a =5;
test1 *t3 = new test1();
t3 = new test2();
t3->fun3(3,3); //这里会打出hello hangzhou字样,多态体现出来了吧。<strong></strong>
//t2.aa = 55;//error: ‘int test1::aa’ is private 直接调用声明为private的变量,编译器也是会报错的。
cout<<"t2.a:"<<t2.a<<endl;
t2.fun1(1,2);
t2.fun2(3,4);
t2.fun3(5,6);
cout<<"sizeof t2.pub:"<<sizeof(t2.pub)<<endl; //这里直接访问了pub域,说明成员属性默认是public
cout<<"sizeof test1:"<<sizeof(struct test1)<<endl;
cout<<"sizeof test2:"<<sizeof(struct test2)<<endl;
cout<<"sizeof t2.pub:"<<sizeof(t2.c)<<endl;
}
打印结果:
test1
test1
test2
test1
test1
test2
hello hangzhou
t2.a:5
hello shanghai
hello beijing
hello hangzhou
sizeof t2.pub:4
sizeof test1:24
sizeof test2:32
sizeof t2.pub:4
2)打印结果发现
1.开始处交替打出了test1,test2类的构造函数名字,说明在每次定义一个对象时都会调用相应的构造函数,这个性质和类是一致的。
2.定义的是父类指针,而new的是子类对象,由于存在虚函数,就打印出子类的hangzhou字样。
3.在打印大小的时候,我特意打出test1域pub大小,一来可以验证C++类默认属性是public,而来可以看看编译器安排test1中int对齐大小,这里看是4字节。同样test2类也是将int4字节对齐。针对test1类对象大小,4个int型数据域加上一个虚函数表指针就是24个,即4*4+8=24.同理test2对象多出8个字节的数据域,所以大小是32个字节。
4.C++结构体,如果声明的有虚函数,那么同样存在虚表,而且在内存分布上,依然会安排在类实例的首地址。
3) C++结构体也有虚表:
可以发现虚表和类的没有任何区别。
Vtable for test1
test1::_ZTV5test1: 3u entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI5test1)
16 test1::fun3
Class test1
size=24 align=8
base size=24 base align=8
test1 (0x7f4610929850) 0
vptr=((& test1::_ZTV5test1) + 16u)
Vtable for test2
test2::_ZTV5test2: 3u entries
0 (int (*)(...))0
8 (int (*)(...))(& _ZTI5test2)
16 test2::fun3
Class test2
size=32 align=8
base size=32 base align=8
test2 (0x7f461096d1c0) 0
vptr=((& test2::_ZTV5test2) + 16u)
test1 (0x7f461096d230) 0
primary-for test2 (0x7f461096d1c0)
2.C语言中结构体
1)c的结构体当中不允许有函数存在(有函数指针很正常吧),所以c的结构体就没有了构造函数,析构函数,虚函数等。
2)c的结构体成员变量的访问权限只能是public。
3)c的结构体不允许继承的,编译起来会报错的。
3.C++中类继承结构体
在写下面这个例子的时候遇到了点麻烦,本来一开始我想把普通的静态函数hehe1,hehe2,与类写在同一个命名空间,但是写在同一命令空间,在类构造函数中给fun1和fun2赋值的时候,总是遇到错误,因此就分成两个命名空间,这时类test实例内存开始处存放的就是这两个函数指针的值。这样的用法,在android framework用法不少,大家多百度百度,理解深入一点。
#include<iostream>
using namespace std;
namespace arm{
typedef struct test1{
void (*fun1)(void);
void (*fun2)(void);
}callback_t;
extern "C" {
static void hehe1(void){
cout<<"hehe1"<<endl;
}
static void hehe2(void){
cout<<"hehe2"<<endl;
}
}
}
namespace TI{
class test:public arm::callback_t
{
public:
test(){
this->fun1 = arm::hehe1;
this->fun2 = arm::hehe2;
}
~test(){}
};
}
int main(){
TI::test *t1 = new TI::test();
(t1->fun1)();
(t1->fun2)();
cout<<"hehe1:"<<(void*)arm::hehe1<<endl;
cout<<"hehe2:"<<(void*)arm::hehe2<<endl;
return 0;
}
打印结果:
hehe1
hehe2
hehe1:0x4008f2
hehe2:0x400914
调试结果
(gdb) p t1->fun1
$1 = (void (*)(void)) 0x4008f2 <arm::hehe1()>
(gdb) p t1->fun2
$2 = (void (*)(void)) 0x400914 <arm::hehe2()>
(gdb) p hehe1
$3 = {<text variable, no debug info>} 0x4008f2 <arm::hehe1()>
(gdb) p hehe2
$4 = {<text variable, no debug info>} 0x400914 <arm::hehe2()>
(gdb) p *t1
$5 = {<arm::just> = {fun1 = 0x4008f2 <arm::hehe1()>, fun2 = 0x400914 <arm::hehe2()>}, <No data fields>}
上面打印结果和我们调试结果是一致的,C++发现结构体和类基本没什么区别吧。
3.C++类和结构体区别
1)C++类的内部成员变量和成员函数访问级别可有public,protected,private三种,默认情况下是private。而C++结构体内部成员变量和成员方法默认的访问级别是public。
2)C++类的继承默认是private,而结构体的继承默认是public。