动态类型与静态类型

静态类型

是指不需要考虑表达式的执行期语义,仅分析程序文本而决定的表达式类型。静态类型仅依赖于包含表达式的程序文本的形式,而在程序运行时不会改变。通俗的讲,就是上下文无关,在编译时就可以确定其类型。

动态类型

是指由一个左值表达式表示的左值所引用的最终派生对象的类型。例:如果一个静态类型为“类 B ”的指针p 指向一个继承于 B的类 D 的对象,则表达式 *p 的动态类型为“D”。引用按照相似规则处理。一般地讲,基类的指针和基类引用有可能为动态类型,就是说在运行之前不能够确定其真实类型。通常我们说,“基类指针指向的对象的实际/真正类型”或“基类引用所引用的对象的实际/真正类型”,就是它们的动态类型。很显然,这个动态类型是  C++ 语言通过指针和引用实现运行时多态能力的核心概念。

动态绑定与静态绑定

静态绑定:编译时绑定,通过对象调用
动态绑定:运行时绑定,通过地址实现


只有采用“指针->函数()”或“引用变量.函数()”的方式调用C++类中的虚函数才会执行动态绑定。对于C++中的非虚函数,因为其不具备动态绑定的特征,所以不管采用什么样的方式调用,都不会执行动态绑定。
即所谓动态绑定,就是基类的指针或引用有可能指向不同的派生类对象,对于非虚函数,执行时实际调用该函数的对象类型即为该指针或引用的静态类型(基类类型);而对于虚函数,执行时实际调用该函数的对象类型为该指针或引用所指对象的实际类型。比如下面代码:
class Base {
public:
void func() {
cout << "func() in Base." << endl;
}
virtual void test() {
cout << "test() in Base." << endl;
}
}; class Derived : public Base {
void func() {
cout << "func() in Derived." << endl;
}
virtual void test() {
cout << "test() in Derived." << endl;
}
}; int main() {
Base* b;
b = new Derived();
b->func();
b->test();
}

由运行结果可以看到,b是一个基类指针,它指向了一个派生类对象,基类Base里面有两个函数,其中test为虚函数,func为非虚函数。因此,对于test就表现为动态绑定,实际调用的是派生类对象中的test,而func为非虚函数,因此它表现为静态绑定,也就是说指针类型是什么,就会调用该类型相应的函数。

虚函数、动态绑定、运行时多态之间的关系


简单地说,虚函数是动态绑定的基础;动态绑定是实现运行时多态的基础。
要触发
动态绑定,需满足两个条件:

(1)  只有虚函数才能进行动态绑定,非虚函数不进行动态绑定。

(2)  必须通过基类类型的引用或指针进行函数调用。
通过基类指针或基类引用做形参,当实参传入不同的派生类(或基类)的指针或引用,在函数内部触发
动态绑定,从而来
运行时
实现多态的。
 
下面通过实际例子才展示运行时多态的实现方式:
如下代码是一个Base基类和它的三个派生类Derived1,Derived2,Derived3。
class Base {
public:
void Print() {
cout << "Print() from Base." << endl;
} virtual void Display() {
cout << "Display() from Base." << endl;
}
}; class Derived1 : public Base {
public: void Print() {
cout << "Print() from Derived1." << endl;
} void Display() {
cout << "Display() from Derived2." << endl;
}
}; class Derived2 : public Base {
public:
void Print() {
cout << "Print() from Derived2." << endl;
} void Display() {
cout << "Display() from Derived2." << endl;
}
}; class Derived3 : public Base {
public:
void Print() {
cout << "Print() from Derived3." << endl;
} void Display() {
cout << "Display() from Derived3." << endl;
}
};
下面两个全局函数分别以基类指针和基类引用作形参来实现运行时多态:
//通过基类引用作形参实现多态
void Polymorphic1(Base& b) {
b.Print();
b.Display();
} //通过基类指针作形参实现多态
void Polymorphic2(Base* b) {
b->Print();
b->Display();
}

下面是测试代码:

int main() {
Base b;
Derived1 d1;
Derived2 d2;
Derived3 d3; vector<Base> base_vec;
base_vec.push_back(b);
base_vec.push_back(d1);
base_vec.push_back(d2);
base_vec.push_back(d3); vector<Base*> base_ptr_vec;
base_ptr_vec.push_back(&b);
base_ptr_vec.push_back(&d1);
base_ptr_vec.push_back(&d2);
base_ptr_vec.push_back(&d3); cout << endl << "对通过基类引用作形参实现多态进行测试" << endl;
//对通过基类引用作形参实现多态进行测试 (测试方式错误)
for (int i = 0; i != base_vec.size(); ++i) {
Polymorphic1(base_vec[i]);
} cout << endl << "对通过基类指针作形参实现多态进行测试" << endl;
//对通过基类指针作形参实现多态进行测试
for (int i = 0; i != base_vec.size(); ++i) {
Polymorphic2(base_ptr_vec[i]);
} cout << endl << "对通过基类引用作形参实现多态进行测试" << endl;
//对通过基类引用作形参实现多态进行测试
Polymorphic1(b);
Polymorphic1(d1);
Polymorphic1(d2);
Polymorphic1(d3); return 0;
}

测试结果如下图:




        我们看到第一组想对通过基类引用作形参实现多态进行测试,需要将不同派生类的对象作实参传过去。然而,把派生类放到基类的vector中存储的过程中,派生类对象被自动转换为基类对象了,因而实际存储的均为基类对象,所以再从vector中取出对象元素做实参传递的时候,传递的均为基类对象,所以测试失败。
        对这个比较挫的测试进行的分析和思考:
由C++ STL的vector容器中存储的对象拷贝引起的对capacity属性 的理解
        而下面两组我们分别把不同派生类的指针和对象作实参进行测试,结果显示均实现了运行时多态:即传入不同的对象,就会调用该对象相应的Display函数,因为在基类中,Display为虚函数,所以这里它实现了对象的动态绑定,从而实现了运行时多态;与之做对比的Print函数在基类中为非虚构函数,因此对Print函数不会进行动态绑定,而是静态绑定:即基类指针只能调用基类中的Print函数。


参考文献:

1. http://blog.csdn.net/lynnboy/article/details/154894
2. http://www.cnblogs.com/chris12/archive/2012/10/28/2744131.html
3. http://blog.csdn.net/livelylittlefish/article/details/2171521

            

          原创文章,转载请注明: 转载自  IIcyZhao's Road

本文链接地址:  http://blog.csdn.net/iicy266/article/details/11906509

C++中的动态类型与动态绑定、虚函数、运行时多态的实现的更多相关文章

  1. Objective-C路成魔【11-多态性、动态类型和动态绑定】

    郝萌主倾心贡献.尊重作者的劳动成果,请勿转载. 假设文章对您有所帮助,欢迎给作者捐赠.支持郝萌主.捐赠数额任意,重在心意^_^ 我要捐赠: 点击捐赠 Cocos2d-X源代码下载:点我传送 多态这个其 ...

  2. Python中高层次的数据结构,动态类型和动态绑定,使得它非常适合于快速应用开发,也适合于作为胶水语言连接已有的软件部件。

    https://github.com/jhao104/proxy_pool/blob/master/doc/introduce.md 3.代码模块 Python中高层次的数据结构,动态类型和动态绑定, ...

  3. 为什么说OC是运行时语言?什么是动态类型、动态绑定、动态加载?

    转载:https://www.cnblogs.com/dxb123456/p/5525343.html 动态: 主要是将数据类型的确定由编译时,推迟到了运行时. 这个问题其实浅涉及到两个概念,运行时和 ...

  4. C# 中的动态类型

    翻译自 Camilo Reyes 2018年10月15日的文章 <Working with the Dynamic Type in C#> [1] .NET 4 中引入了动态类型.动态对象 ...

  5. C++ //多态 //静态多态:函数重载 和 运算符重载 属于静态多态 ,复用函数名 //动态多态:派生类和虚函数实现运行时多态

    1 //多态 2 //静态多态:函数重载 和 运算符重载 属于静态多态 ,复用函数名 3 //动态多态:派生类和虚函数实现运行时多态 4 5 //静态多态和动态多态的区别 6 //静态多态的函数地址早 ...

  6. JavaScript中两种类型的全局对象/函数【转】

    Snandy Stop, thinking is the essence of progress. JavaScript中两种类型的全局对象/函数 这里所说的JavaScript指浏览器环境中的包括宿 ...

  7. 深入浅出OOP(三): 多态和继承(动态绑定/运行时多态)

    在前面的文章中,我们介绍了编译期多态.params关键字.实例化.base关键字等.本节我们来关注另外一种多态:运行时多态, 运行时多态也叫迟绑定. 运行时多态或迟绑定.动态绑定 在C#语音中,运行时 ...

  8. iOS开发笔记系列-基础3(多态、动态类型和动态绑定)

    多态:相同的名称,不同的类 使不同的类共享相同方法名称的能力成为多态.它让你可以开发一组类,这组类中的每一个类都能响应相同的方法名.每个类的定义都封装了响应特定方法所需要的代码,这使得它独立于其他的类 ...

  9. OC基础6:多态、动态类型和动态绑定

    "OC基础"这个分类的文章是我在自学Stephen G.Kochan的<Objective-C程序设计第6版>过程中的笔记. 1.关于SEL类型的数据: (1).SEL ...

随机推荐

  1. Farming

    Problem Description You have a big farm, and you want to grow vegetables in it. You're too lazy to s ...

  2. Android 操作系统的内存回收机制[转]

    转自:http://www.ibm.com/developerworks/cn/opensource/os-cn-android-mmry-rcycl/ Android APP 的运行环境 Andro ...

  3. python多线程同步

    python多线程同步 作者:vpoet 日期:大约在夏季 import threading import time mylock = threading.RLock() num=0 class my ...

  4. ACdreamOJ 1154 Lowbit Sum (数字dp)

    ACdreamOJ 1154 Lowbit Sum (数位dp) ACM 题目地址:pid=1154" target="_blank" style="color ...

  5. PreferenceFragment 使用 小结

    Perference也就是我们常说的偏好设置,首选项设置,能够自己主动保存一些数据,比如我们在上一次使用的时候的一些内容,则在下一次启动后依旧生效,而不须要再进行配置.当用户改变设置时,系统就会更新S ...

  6. Linux shell中的一个问题 ${}带正则匹配的表达式

    目前在准备龙芯项目的PMON,在研究其编译过程的时候,看到一些make 语句,百思不得其解.后来在shell编程中看到一点资料,牵扯到Shell中的正则表达式.故记录下来,以备后来查阅. 问题: 在某 ...

  7. [记录]关于vertical-align单/多选框与说明文字对齐效果

    效果图:    第一张使用label标签,第二张没有使用.. 使用label标签,middle对齐方式的单选框下降了1px 而没有使用label标签,sub对齐方式的 却 居 中 了 =_= 不太理解 ...

  8. mapkit定位以及俯视视图

    1.导入框架MapKit.framework,CoreGraphics.framework

  9. Xcode中插件的安装以及Xcode升级后插件实效的解决方法

    插件的安装 下载好插件,直接运行,然后将Xcode关闭,再次打开Xcode会弹出一个提醒框. 这时候选择 Load Bundle 即可,这时候插件就安装到了Xcode上. Xcode所有的插件都安装在 ...

  10. <转>GC其他:引用标记-清除、复制、标记-整理的说明

    注:本文根据<深入理解Java虚拟机>第3章部分内容整理而成. 对象死亡历程 1.基本的mark&sweep是必须的,后续的都是对他的改进, 2.young代理的survivor就 ...