c++静态绑定和动态绑定以及虚函数中缺省参数的绑定

c++语言的一个重要特性就是支持多态,多态性可以简单的概括为"一种接口,不同实现",是面向对象的核心,可分为类的多态和函数的多态,其中函数的多态可以理解为非虚函数的重载,类的多态可以通过虚函数实现。前者相对简单,通过参数的类型和参数的个数来实现静态绑定(从最佳匹配到次佳匹配)。后者的具体实现可以参照博文http://blog.csdn.net/caoyan_12727/article/details/51160690,里面有详细的讲解,下面我们来明确下面的四个概念:

(1).对象的静态类型:指针或是引用在声明是所采用的类型,在编译期就能确定;

(2).对象的动态类型:因为可以用基类指针或是引用指向派生类对象,所以指针或引用的动态类型由它所指的实际类型确定,在运行期决定;对象的静态类型不可更改,而动态类型却可以改变;假设我们有如下类型定义:

class B{
};
class C : public B{
};
class D : public B{
};
D* pD = new D(); // pD的静态类型是它声明的类型D*,动态类型也是D*                     
B* pB = pD;// pB的静态类型是它声明的类型B*,动态类型是pB所指向的对象pD的类型D*  
C* pC = new C(); 
pB = pC;//pB的动态类型是可以更改的,现在它的动态类型是C* 

(3).静态绑定:

静态绑定是指在程序编译过程中,把函数(方法或者过程)调用与响应调用所需的代码结合的过程称之为静态绑定。

(4)动态绑定:

动态绑定是指在执行期间(非编译期间)判断所引用(或指向)对象的实际类型,根据实际类型调用其相应的方法,程序运行过程中,把函数(或过程)调用与响应调用所需要的代码相结合的过程称之为动态绑定。

举个简单的例子:

输出结果为:

class B{
public:
	void f(){
		cout << "non-virtual function of class B"<< endl;
	};
	virtual void vf(){
		cout << "virtual function of class B" << endl;
	};
};
class C : public B{
public:
	void f(){
		cout << "non-virtual function of class C" << endl;
	};
	virtual void vf(){
		cout << "virtual function of class C" << endl;
	};
};
class D : public C{
public:
	void f(){
		cout << "non-virtual function of class D" << endl;
	};
	virtual void vf(){
		cout << "virtual function of class D" << endl;
	};
};

int main(){
	D* pD = new D();
	B* pB = pD;
	//非虚函数
	pD->f();
	pB->f();
	//虚函数
	pD->vf();
	pB->vf();
	return 0;
}
输出结果为:


如果我们将类D的函数f注释掉时:

class D : public C{
public:
	//void f(){
	//	cout << "non-virtual function of class D" << endl;
	//};
	virtual void vf(){
		cout << "virtual function of class D" << endl;
	};
};
int main(){
	D* pD = new D();
	B* pB = pD;
	//非虚函数
	pD->f();
	return 0;
}
运行结果:


从上面的结果来看,非虚函数的执行大概遵循下面的原则:

(1)发生的是静态绑定,即根据指针的静态类型就可以确定哪个函数在编译器被绑定,而虚函数执行时,发生动态绑定时,一定要根据指针(引用)的动态类型来确定;

(2)在静态绑定时首先查看当前类(比如class D)有没有f()函数,如果有那么执行类D自己的函数f(),如果没有就会从继承层次上从下至上寻找,直到找到匹配为止(比如在类D中没有定义f(),编译器将类C中对应的方法和f()绑定,所以最终执行类C中的f()函数)。这一切都在编译器时决定;

对于虚函数而言,

pD->vf();
pB->vf();
函数vf()的执行,仅仅从pD和pB静态类型是不能确定哪个vf()对应的方法被绑定执行,用引用(或指针)调用的虚函数在运行时确定,被调用的函数是引用(或指针)所指对象的实际类型所定义的。C++中为了实现虚函数的动态绑定,一般是通过一张虚函数表(virtual table)实现的。这个表中记录了虚函数的地址,解决继承、覆盖的问题,保证动态绑定时能够根据对象的实际类型调用正确的函数。

缺省参数的虚函数绑定:

当缺省参数和虚函数一起出现的时候情况有点复杂,情况却不同:虚函数是动态绑定的,但是为了执行效率,缺省参数是静态绑定的。让我们来看看一个实例:

我们可以看到:
(1)对于pa->func(),pa是一个基类指针,指向派生类对象,func()是虚函数,如果发生的是动态绑定,则会执行派生类对象的func函数,则val的参数理论上应该是0。但是因为发生的是静态绑定,所以执行的是pa指针的静态类型所对应的func函数(也就是基类的虚函数func,此时val的参数值是1)。
(2)对于p->test(),由于p是派生类指针指向派生类对象,所以执行派生类对象的test()函数,但是派生类中没有定义test()函数,只能执行从基类继承而来的函数test(),而基类test()执行的是基类的func(),所以输出1;
(3)对于p->func(),发生静态绑定,执行指针p的静态类型所关联对象的func()函数,所以此时val的值是0;
综上所述,以上四个名词的关系(静态类型,动态类型,静态绑定,动态绑定)可以表示如下:

如果要发生动态绑定,那么虚函数和指针(引用)动态类型的存在这两个关键因素缺一不可:
(1)如果指针或引用只存在动态类型(即此时它们指向了派生类的对象)而没有虚函数的存在,那么函数(或方法)的调用只和其静态类型有关(即使它此时存在动态类型,在这里我们定义的存在动态类型是指基类指针(引用)指向派生类对象了),就像第一个例子的f()的调用一样;
(2)如果只有虚函数,而基类指针却没有指向派生类对象(此时指针只存在静态类型),此时通过基类指针调用虚函数pB->vf()发生的依然是静态绑定,也就是说函数(方法)的调用只和指针的静态类型有关。
以上都是个人理解,存在什么不足欢迎讨论!!!!!!


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值