在c++中,继承的概念可以理解为c中得嵌套结构体,对于各种函数,类中的成员函数,类中的友元函数,各种继承的虚拟函数,只要从编译器的角度去理解
就会变得简单。例如下例:
class D{
public:
int func_D();
int v_func_DD();
};
class D1 : public D{
public:
int func_D1();
virtual int v_func_DD();
};
class D2 : public D1{
public:
int func_D2();
int v_func_DD();
int d1;
};
//....
函数的具体实现略
//....
int main(int argc, char **argv){
D2 dd2;
D *d_ptr;
D1 *d1_ptr;
D2 *d2_ptr;
d_ptr = &dd2;
d1_ptr = &dd2;
d2_ptr = &dd2;
d_ptr->v_func_DD();//①
d1_ptr->v_func_DD();//②
d1_ptr->func_D2();//③
d2_ptr->func_D2();//④
return 0;
}
在主函数中,只有一个对象实例,即class D2类型的dd2;D2类直接继承自D1,间接继承自D,因为可以用这两种类型的指针引用D2类型的对象实例。
从编译器角度来看时,对于①处,编译器将做如下处理:
1.判断指针类型,发现d_ptr的类型是class D。
2. 在class D中寻找 v_func_D函数的定义,如果没有定义,将报错。
3. 如果有一个定义,判断是不是虚函数。
3.1 如果不是虚函数(正如本例那样),则在全局符号表中找到相应的修饰后符号,假设为v_func_DD_classD_void,并根据符号对应的函数地址调用该函数。
3.2 如果在class D中v_func_DD被定义为虚函数,则需要动态绑定。
于是,在①处,由于v_func_DD并没有在class D中被定义为虚拟函数,则编译器将判断该调用为int D::v_func_DD(),这里与调用一个全局函数并无区别。
对于②处的函数调用,编译器将会做出相同处理:
1.判断指针类型,发现d1_ptr的类型是class D1。
2. 在class D1中寻找 v_func_D函数的定义,如果没有定义,将报错。
3. 如果有一个定义,判断是不是虚函数。
3.1 如果不是虚函数,则在全局符号表中找到相应的修饰后符号,此时为虚函数,跳过此步。
3.2 在class D1中v_func_DD被定义为虚函数,则需要动态绑定,在动态绑定时,由于d1_ptr绑定的对象是D2类型,而此时D2类型的虚函数表已经建立好,并填入了
int D2::v_func_DD()的地址,所以这里调用的真正函数是int D2::v_func_DD()。
对于③处的处理,编译器判断指针类型d1_ptr为class D1,而D1中并没有func_D2的定义,即是d1_ptr绑定的是D2对象,编译器也是不允许这种情况的,所以编译器将报
未定义的错误。
对于④的处理,由于指针是class D2类型,因此会正确调用int D2::func_D2()函数,这是,调用该成员函数和调用一个全局函数的情况并无区别,只不过编译器根据类型名,
参数名,和函数名对最后生成的全局符号进行了mangling。