#include<iostream>
using namespace std;
void aaa(){
cout<<"aa";
}
int main(){
class a{
public:
int a=10;
int b=4;
void func1(){
cout<<"func1 from a"<<endl;
}
void func2(){
cout<<"func2 from a without param"<<endl;
}
virtual void func3(){
cout<<"func3 from a"<<endl;
}
virtual void func4(){
cout<<"func4 from a"<<endl;
}
};
class b:public a{
public:
virtual void func1(){
cout<<"func1 from b"<<endl;
}
virtual int func2(int a){
cout<<"func2 from b with param a"<<a<<endl;
return 1;
}
virtual void func3(){
cout<<"func3 from b"<<endl;
}
virtual void func5(){
cout<<"func5 from b"<<endl;
}
};
a obj1;
b obj2;
obj1.func1();
obj2.func1();
obj1.func4();
obj1.func2();
obj2.func2(10); //这两个例子都是重载,最大的一个特点就是可以改变参数列表,也可以改变返回值
obj1.func4();
/*
重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
*/
//我们主要看看虚函数和重写的区别
//虚函数其实也是重写的一种
//那么虚函数和重写最大的一个区别在哪里呢
/*
考虑一个情况
在java中,线程的模型是,用户自己继承一个系统的thread类,然后用户通过重写这个类的run方法,动态调用
例如:
class my_thread : Thread{..实现..};
list<thread>.push_back( my_thread(..) );
如果我们只是单纯的重写,而不设置为virtual,会是什么效果呢?
*/
a *obj3=new b; //b类假设是我们重写完成的类,现在需要运行这个类
obj3->func1(); //它实际上这里是调用的基它实际上这里是调用的基类的func1,因为他是a类型【基类类型】的指针,虽然他指向的是b对象
//这就不能达到,我们在程序运行的时候,写了一个新类,指针指过来,运行的目的了
//唯一的解决办法就是把这个函数声明成为virtual虚函数
obj3->func3(); //因为func3是虚函数,他会去虚函数表里面查看,确定运行的是b的func3()
//因此,虚函数如果要使用,就要使用指针,因为只有指针才能实现基类指针指向派生类对象的操作
obj3->func4();
cout<<"------------------------------------------"<<endl;
//2。探究虚函数表
//虚函数表的解释网上有很多,就是在类的第一个字节那里存放了一个虚函数表的地址,是一个“函数指针的数组”
typedef void(*pfun)(void); //定义一个void(void)型的函数指针
void *p2=*(int***)obj3; //访问第一个虚函数
p2=*(int**)p2;
((pfun)p2)(); //访问函数
p2=*(int***)obj3+1; //访问下一个虚函数
p2=*(int**)p2;
((pfun)p2)(); //访问函数
p2=*(int***)obj3+2; //访问下一个虚函数
p2=*(int**)p2;
((pfun)p2)(); //访问函数
p2=*(int***)obj3+3; //访问下一个虚函数
p2=*(int**)p2;
((pfun)p2)(); //访问函数
p2=*(int***)obj3+4; //访问下一个虚函数
p2=*(int**)p2;
((pfun)p2)(); //访问函数
return 0;
}