C++中的静态绑定和动态绑定
C++在面向对象编程中,存在着静态绑定和动态绑定的定义,本节即是主要讲述这两点区分。
我是在一个类的继承体系中分析的,因此下面所说的对象一般就是指一个类的实例。
首先我们需要明确几个名词定义:
- 静态类型:对象在声明时采用的类型,在编译期既已确定;
- 动态类型:通常是指一个指针或引用目前所指对象的类型,是在运行期决定的;
- 静态绑定:绑定的是静态类型,所对应的函数或属性依赖于对象的静态类型,发生在编译期;
- 动态绑定:绑定的是动态类型,所对应的函数或属性依赖于对象的动态类型,发生在运行期;
从上面的定义也可以看出,非虚函数一般都是静态绑定,而虚函数都是动态绑定(如此才可实现多态性)。
先看代码和运行结果:
class A
{
public:
/*virtual*/ void func(){ std::cout << "A::func()\n"; }
};
class B : public A
{
public:
void func(){ std::cout << "B::func()\n"; }
};
class C : public A
{
public:
void func(){ std::cout << "C::func()\n"; }
};
下面逐步分析测试代码及结果,
C* pc = new C(); //pc的静态类型是它声明的类型C*,动态类型也是C*;
B* pb = new B(); //pb的静态类型和动态类型也都是B*;
A* pa = pc; //pa的静态类型是它声明的类型A*,动态类型是pa所指向的对象pc的类型C*;
pa = pb; //pa的动态类型可以更改,现在它的动态类型是B*,但其静态类型仍是声明时候的A*;
C *pnull = NULL; //pnull的静态类型是它声明的类型C*,没有动态类型,因为它指向了NULL;
如果明白上面代码的意思,请继续,
pa->func(); //A::func() pa的静态类型永远都是A*,不管其指向的是哪个子类,都是直接调用A::func();
pc->func(); //C::func() pc的动、静态类型都是C*,因此调用C::func();
pnull->func(); //C::func() 不用奇怪为什么空指针也可以调用函数,因为这在编译期就确定了,和指针空不空没关系;
如果注释掉类C中的func函数定义,其他不变,即
class C : public A
{
}; pa->func(); //A::func() 理由同上;
pc->func(); //A::func() pc在类C中找不到func的定义,因此到其基类中寻找;
pnull->func(); //A::func() 原因也解释过了;
如果为A中的void func()函数添加virtual特性,其他不变,即
class A
{
public:
virtual void func(){ std::cout << "A::func()\n"; }
}; pa->func(); //B::func() 因为有了virtual虚函数特性,pa的动态类型指向B*,因此先在B中查找,找到后直接调用;
pc->func(); //C::func() pc的动、静态类型都是C*,因此也是先在C中查找;
pnull->func(); //空指针异常,因为是func是virtual函数,因此对func的调用只能等到运行期才能确定,然后才发现pnull是空指针;
分析:
在上面的例子中,
1. 如果基类A中的func不是virtual函数,那么不论pa、pb、pc指向哪个子类对象,对func的调用都是在定义pa、pb、pc时的静态类型决定,早已在编译期确定了。
同样的空指针也能够直接调用no-virtual函数而不报错(这也说明一定要做空指针检查啊!),因此静态绑定不能实现多态;
2. 如果func是虚函数,那所有的调用都要等到运行时根据其指向对象的类型才能确定,比起静态绑定自然是要有性能损失的,但是却能实现多态特性;
本文代码里都是针对指针的情况来分析的,但是对于引用的情况同样适用。
至此总结一下静态绑定和动态绑定的区别:
1. 静态绑定发生在编译期,动态绑定发生在运行期;
2. 对象的动态类型可以更改,但是静态类型无法更改;
3. 要想实现动态,必须使用动态绑定;
4. 在继承体系中只有虚函数使用的是动态绑定,其他的全部是静态绑定;
建议:
绝对不要重新定义继承而来的非虚(non-virtual)函数(《Effective C++ 第三版》条款36),因为这样导致函数调用由对象声明时的静态类型确定了,而和对象本身脱离了关系,没有多态,也这将给程序留下不可预知的隐患和莫名其妙的BUG;
另外,在动态绑定也即在virtual函数中,要注意默认参数的使用。当缺省参数和virtual函数一起使用的时候一定要谨慎,不然出了问题怕是很难排查。
看下面的代码:
class E
{
public:
virtual void func(int i = )
{
std::cout << "E::func()\t"<< i <<"\n";
}
};
class F : public E
{
public:
virtual void func(int i = )
{
std::cout << "F::func()\t" << i <<"\n";
}
}; void test2()
{
F* pf = new F();
E* pe = pf;
pf->func(); //F::func() 1 正常,就该如此;
pe->func(); //F::func() 0 哇哦,这是什么情况,调用了子类的函数,却使用了基类中参数的默认值!
}
为什么会有这种情况,请看《Effective C++ 第三版》 条款37。
这里只给出建议:
绝对不要重新定义一个继承而来的virtual函数的缺省参数值,因为缺省参数值都是静态绑定(为了执行效率),而virtual函数却是动态绑定。
C++中的静态绑定和动态绑定的更多相关文章
- java中的静态绑定与动态绑定
http://blog.csdn.net/u012420654/article/details/51945853 http://blog.csdn.net/zhangjk1993/article/de ...
- (C++)C++多态性中的静态绑定和动态绑定
静态绑定和动态绑定是C++多态性的一种特性. 1.对象的静态类型和动态类型: 对象的静态类型: 对象在声明是采用的类型,在编译期确定: 对象的动态类型: 当前对象所指的类型,在运行期决定,对象的动态类 ...
- 【校招面试 之 C/C++】第8题 C++中的静态绑定与动态绑定
转自:https://blog.csdn.net/chgaowei/article/details/6427731 做了部分修改 为了支持c++的多态性,才用了动态绑定和静态绑定.理解他们的区别有 ...
- Java静态绑定与动态绑定
程序绑定的概念: 绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来.对java来说,绑定分为静态绑定和动态绑定:或者叫做前期绑定和后期绑定. 静态绑定: 在程序执行前方法已经被绑定(也就是说 ...
- Java方法的静态绑定与动态绑定讲解(向上转型的运行机制详解)
转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6554103.html 一:绑定 把一个方法与其所在的类/对象 关联起来叫做方法的绑定.绑定分为静态绑定(前期绑 ...
- Java字节码里的invoke操作&&编译时的静态绑定与动态绑定
一个一直运行正常的应用突然无法运行了.在类库被更新之后,返回下面的错误. Exception in thread "main" java.lang.NoSuchMethodErro ...
- java — 静态绑定和动态绑定
绑定:一个方法的调用与方法所在的类关联起来.java中的绑定分为静态绑定和动态绑定,又被称作前期绑定和后期绑定. 静态绑定:(final.static.private)在程序执行前已经被绑定,也就是说 ...
- C++中虚函数的动态绑定和多态性
目录 静态类型 vs 动态类型.静态绑定 vs 动态绑定 虚函数动态绑定实现机制.虚析构函数 多态性 一.静态 vs 动态 静态类型 VS 动态类型.静态类型指的是对象声明的类型,在编译器确定的.动态 ...
- Java静态绑定和动态绑定
程序绑定的概念: 绑定指的是一个方法的调用与方法所在的类(方法主体)关联起来.对java来说,绑定分为静态绑定和动态绑定:或者叫做前期绑定和后期绑定 静态绑定(早绑定 编译器绑定): 在程序执行前方法 ...
随机推荐
- 奇怪吸引子---Sakarya
奇怪吸引子是混沌学的重要组成理论,用于演化过程的终极状态,具有如下特征:终极性.稳定性.吸引性.吸引子是一个数学概念,描写运动的收敛类型.它是指这样的一个集合,当时间趋于无穷大时,在任何一个有界集上出 ...
- LDO/DC-DC区别总结(转)
电源是一个电子系统中不可缺少的非常重要的一部分.但是外接的电源通常不能够完全提供系统中需要的所有的电源种类.因此带来了电源电压的变换问题.常用的电源电压的变换芯片包括LDO和DC-DC两种.下面对这两 ...
- Homebrew
Homebrew官网:http://brew.sh Homebrew installs the stuff you need that Apple didn't Homebrew的安装非常简单,打开终 ...
- appToken 来源
.appToken的来源ActivityRecord, 用于在ActivityManagerService中记录Activity相关的信息(ActivityThread端 用的是ActivityCli ...
- 阿里前DBA的故事
别人怎么享受生活,与你无关.你怎么磨砺与你有头.引用同事周黄江的一句话,很多人努力程度还远没有到拼天赋的时候. 成功的人都是那种目标很明确的人.对于文中厨师的经历很感兴趣.不管是IT还是餐饮,哪个行业 ...
- JavaWeb(李兴华著)开发笔记
1.Java语法-基础 环境变量-JAVA_HOME, PATH, ClassPath 变量名 作用 举例 JAVA_HOME 指向JDK目录 C:\Program Files\Java\jdk1.7 ...
- easyui plugin——etreegrid:CRUD Treegrid
昨天写了一个ko+easyui的同样的实现,感觉写的太乱,用起来十分麻烦,于是今天照着edatagrid,写了一个etreegrid,这样再用ko绑定就方便多了. 使用很简单,$(tableId).e ...
- 使用SQL联合查询来构建临时vo对象的应用
联合查询: 表1: team球队表 表2:schedule 赛程表 需要数据: 球队名称.主队ID.主队名称.客队ID.客队名称.胜负情况 方法1. Object数组取出列和数值 import jav ...
- EDM备忘录:触发式邮件订阅和退订功能介绍
一般来说,有触发式邮件订阅和退订功能是邮件模板设计中必不可少的两项功能,下面博主为大家介绍一下. 若客户在订阅后不想再收到这类邮件即可选择退订,将不再收到该IP地址的推广邮件,避免客户在继续收到这类邮 ...
- 转载 jQuery的三种$()
$号是jQuery“类”的一个别称,$()构造了一个jQuery对象.所以,“$()”可以叫做jQuery的构造函数(个人观点,呵呵!). 1.$()可以是$(expresion),即css选择器 ...