动态类型与静态类型

静态类型

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

动态类型

是指由一个左值表达式表示的左值所引用的最终派生对象的类型。例:如果一个静态类型为“类 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++工作笔记- C++中的动态类型与动态绑定、虚函数、运行时多态的实现相关推荐

  1. [.NET] 《Effective C#》快速笔记 - C# 中的动态编程

    <Effective C#>快速笔记 - C# 中的动态编程 静态类型和动态类型各有所长,静态类型能够让编译器帮你找出更多的错误,因为编译器能够在编译时进行大部分的检查工作.C# 是一种静 ...

  2. 解析Objective-C中多态、动态类型和动态绑定

    解析Objective-C中多态.动态类型和动态绑定 转自http://mobile.51cto.com/iphone-285157.htm Objective-C中多态.动态类型和动态绑定是本文要介 ...

  3. 学习笔记 | c++中四种类型cast(强制)转换

    c++中四种cast转换 C++四种强制转换方式,应用场景,细节 1.const_cast 用于将const变量转为非const. 用来修饰类型的const或volatile属性.除了去掉const或 ...

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

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

  5. java 运行时类型_Java基础之RTTI 运行时类型识别

    运行时类型识别(RTTI, Run-Time Type Identification)是Java中非常有用的机制,在Java运行时,RTTI维护类的相关信息. 多态(polymorphism)是基于R ...

  6. 动态库、静态库、运行时库、引入库之间的区别

    动态库.静态库.运行时库.引入库之间的区别 杂集.捡对口味的看看吧. 转自:http://lingualspark.blog.sohu.com/94785899.html 运行时库:Unix中一个典型 ...

  7. 避免在派生类中重新定义基类的非虚函数

    我们都知道,在基类中定义虚函数的目的是允许派生类拥有相同接口却可以有不同的实现,通过对象的指针或引用来访问虚函数可以实现运行时的多态.这么说来,在派生类中重定义(override)虚函数是没有任何问题 ...

  8. c++ 多态 运行时多态和编译时多态_C++学习笔记之多态

    多态是面向对象三大特性之一 多态分为两类: 静态多态:函数重载 和 运算符重载 属于静态多态,复用函数名 动态多态:派生类 和 虚函数 实现运行时多态 静态多态和动态多态的区别: 静态多态的函数地址早 ...

  9. mybatis工作笔记002_mybatis中如果返回的结果没有的话默认返回null的list_但可启用returnInstanceForEmptyRow_返回为list不为null但为0条

    技术交流QQ群[JAVA,C,.NET,BigData,AI]:170933152 但是对象是null,然后程序后面判断的时候就出异常了. 有时返回的是list,里面有1件数据,但数据为null,而不 ...

最新文章

  1. three.js 贴图只显示颜色_C4D作品“花里胡哨”?我怀疑你贴图方式有问题……
  2. spring boot application.properties 属性详解
  3. Ant简单工程的构建
  4. jdk8 cms g1gc_G1 vs CMS vs平行GC
  5. 国科大高级人工智能12-博弈
  6. 回调、匿名函数、闭包
  7. ios java 加密_AES加密 - iOS与Java的同步实现
  8. ue4 后期处理景深_珠宝摄影教程——后期修图景深合成(如何完全把珠宝全部拍清晰)...
  9. G2.9 std_alloc源码剖析
  10. 快速乘 (牛客 电音之王)
  11. java项目实战 学生信息管理系统(UI界面+连接数据库)
  12. win7下 Windows Process Activation Service 服务导致的 iis无法启动的解决方法
  13. BootStrap左侧菜单栏
  14. SGE(集群任务管理系统)常用操作命令
  15. CTFSHOW-WEB详解
  16. ukf 在matlab 下的实现,ukf在matlab下的实现
  17. WF(Workflow foundation)与Asp.net结合(二)
  18. matlab的lambda,lambda算法matlab
  19. 自有App小程序第三方微信授权登录的实现
  20. 缓存加速--Squid代理服务器应用(传统代理、透明代理)

热门文章

  1. 数字化转型的4个阶段,从报表到可视化再到数据平台,谁更重要?
  2. 飞鸽传书不能用?这里列出所有解决方法!
  3. C++老话题:用指向函数的指针作函数参数
  4. 『实用』判断一个噩梦客户的7个预警信号
  5. 飞鸽传书:服务器开发系列—系统构架
  6. 15个设计得最糟糕最变态的CAPTCHA验证码
  7. 内存经销商穷困潦倒 七元午饭都赊账
  8. C/C++入门的精髓!太全了吧,收藏夹的必备
  9. 因为加班,错过77万年终大奖,你还加班?
  10. 各个阶级的前端 必须掌握的基本技能汇总