下边要说的是我在语言学习过程中遇到一些不理解的问题,为什么要有virtual机制,还有什么是晚捆绑。我当时学到这的时候可能困惑了一段时间,后来经过继续学习测试等,得出了自己的结论,仅在这记录一下。没有太多的代码实例,更像是我在啰嗦一些东西。
先说virtual
说道virtual肯定就想到多态,virtual是c++ 实现多态的一种方法,为什么要有这样一套虚机制? 还是那些原则,代码复用 ”想用不变的代码去实现不同算法“,大概就是 懒吧。
举个简单的例子,(可能我现在在站的高度不够,理解范围有限,但这个例子至少是虚机制想要解决的问题中的一种)
有一个方法void test() 来测试不同国家的人说的语言,比如说有 Chinese Japanese French 等几个类,类中都有一个speak() 方法
然后可以考虑这样写
void test(Chinese *p)
{
p->speak();
}
void test(Japanese*p)
{
p->speak();
}
void test(French *p)
{
p->speak();
}
这样可以达到测试每个对象所说语种的目的。 但这样看起来很不好,这几个函数,只有参数类型不一样,内部实现都一样。那么想 能不能把参数类型设置成某个特殊的类型,这个特殊的类型可以接收 Chinese Japanese French 等这些类的对象,然后在调用speak时,根据我实际传进去的参数类型,再调用每个类中的speak方法。 如果可以的话就能只写一个函数,实现不同的测试了
void test (special_class *p)
{
p->speak();
}
virtual机制就解决了上述问题,可以弄一个human的基类 然后让Chinese Japanese French这几个类继承它, 然后语法上允许父类指针指向子类对象
void test(human * p)
{
p->speak();
}
然后问题是 test既然是能处理不同子类对象,那它如何确定该调用哪个子类中的speak()?
这就需要 晚捆绑啊,函数覆盖啊 虚表啊什么的那一套来实现,这个不是我困惑的重点,关于虚表可以看看这个
第二个 关于晚捆绑
学到这的时候我很是困惑,有早捆绑 或者叫 静态绑定,比如说上边的test 的参数 只是一个类类型 而不是指针或者引用的话,也就是说它没有多态,只能处理一种类型的对象,
这时候调用speak 就是静态绑定,在编译阶段就能完成。 如果有多态 那就叫晚捆绑(动态绑定) 绑定只能在运行时。困惑就在这了,
什么叫运行时才能绑定啊????
在我的程序运行之前,我的所有代码不是已经的写定了么,编译之后 已经生成了 那些固定的汇编语句了啊,还有什么是非得运行之后才确定么????
说是不知道函数要处理的是哪个具体的子类对象,所以确定不了调用哪个函数, 那既然我调用test的代码已经写了
比如
Chinese a;
test(a);
那函数要处理的对象不就是Chinese 类型的么 那就能确定调用的是Chinese 中的speak()了
编译时就把p->speak(); 换成 call(Chinese中speak的地址) 不就行了么,还有什么是在运行之前不能确定的呢?????
?????????????????????????????????????????????????????????????????????????????
当时确实很困惑啊,后来想想 too young too naive 啊
按照上边的想法,如果只调用test(a);
那是可以 把test函数中的speak调用改成call(Chinese中speak的地址),当程序执行到test(a);这句时 跳到改好的 test()函数的代码部分 能往下执行,并调用了正确的函数。只要再稍微往后想一点, test()都被改成了 针对对象a的特定版本了, 那还能调用它 来处理其他子类对象么? 不能处理了 ,那多态跑哪去了?
可见照上述我所想的这种静态绑定是实现不了多态的。 那多态是如何实现的,是靠动态绑定实现的,在c++编程思想中讲的比较明白了,为了实现多态,编译器在处理时会隐藏的插入一些内容
简单说,就是因为它不知道要处理的对象类型,不能直接去call某个函数的地址,它插入这段代码,如果是有虚机制的对象,那在对象存储空间里能找到v_ptr 也就是虚表指针,这个不管哪个子类的对象只要有virtual 都会有这个指针,然后通过这个指针的偏移处理,在虚函数表中找到要调用的具体版本的函数的地址,再 call 就能实现不管处理什么类型的子对象,都能找到v_ptr都能通过偏移 找到想要的函数,这就实现了 通用的处理。而这种处理方式,需要在程序运行是,找到传进来的那个具体对象的存储空间,然后在里边找到v_ptr 然后计算等等, 然后才能得到想要的函数, 也就可以理解 这是发生在运行时。
如果对这一块有疑惑,推荐去看c++编程思想第二版 第十五章