C++动态类型与静态类型

静态类型

编译时的类型,运行前就确定了,是变量声明时的类型或表达式生成的类型

动态类型

运行时才确定的类型,是变量或表达式表示的内存中的对象的类型

Quote* p = new b_Quote;  // Quote 是基类,b_Quote 是子类

指针 p 的静态类型是 Quote,在编译时已经确定了,但它的动态类型是 b_Quote,运行时才知道

举例

Bulk_quote bulk;
Quote* pQuote = &bulk;
Quote& pQuote = bulk;
double print_total(ostream& os, const Quote& item, size_t n);

三个表达式均为静态类型可能与动态类型不同

其实关于C++的动态绑定就足以写好多篇博客了,但在这篇文章中我不准备深刻剖析C++动态绑定机制(即虚函数机制),只是说明动态绑定和静态绑定的区别,强调C++对象的动态类型和静态类型。

什么是静态类型?什么是动态类型?

       首先这两种肯定是对象(变量)类型的一种说法,而区分这两种类型最直接的方法就是看表达式的形式。关于一个表达式的对象(变量)的静态类型和动态类型需要从两点进行分析:

  1. 该对象(变量)的静态类型和动态类型是否相等;
  2. 如果该对象的静态类型和动态类型不相等,其动态类型是否有作用。

       对于第(1)点,其实我们可以认为每个对象(变量)都存在动态类型和静态类型,只是大多数情况下一个对象的动态类型和静态类型是相等的,所以我们平常都没有强化这两个概念,如表达式int a; 对象a的静态类型和动态类型就是相等的,所以平常我们都是直接称呼变量a的类型是int。对于第(2)点,首先只有当我们的表达式使用的是指针或引用时对象的静态类型和动态类型才有可能不同,而且就算一个对象的静态类型和动态类型不相等,其还不一定能享受到动态类型所带来的方便(即动态绑定),因为我们知道,动态绑定的前提条件是继承体系中的子类继承并重写了基类的虚函数。
       如果觉得上面的解释有点绕,直接解释静态类型和动态类型的定义,一个对象(变量)的静态类型就是其声明类型,如表达式int a中的int就是对象a的声明类型,即静态类型;而一个对象(变量)的动态类型就是指程序执行过程中对象(指针或引用)实际所指对象的类型,如Base* pB = new Drived; 其中class Drived继承于class Base,则指针对象pB的静态类型就是Base(声明类型),动态类型就是Drived(实际所指对象的类型),又如Base* pB = new Base;此时指针对象pB的静态类型和动态类型也是相等的,都是Base。

动态类型的作用

       上面已经说明了,大多数情况下一个对象的静态类型和动态类型都是相等的,而且就算其静态类型和动态类型不相等,也不一定会使动态类型发挥威力,那到底什么时候一个对象(变量)的动态类型能发挥其威力呢?三个条件:(1) 该 对 象 是 指 针 或 引 用 形 式 ; \color{#FF3030}{该对象是指针或引用形式;} (2) 该 对 象 的 静 态 与 动 态 类 型 不 同 ; \color{#FF3030}{该对象的静态与动态类型不同;} (3) 应 用 场 景 为 带 虚 函 数 的 继 承 体 系 结 构 \color{#FF3030}{应用场景为带虚函数的继承体系结构} 。其实你可以直接理解为:动态类型就是为动态绑定(C++继承的多态)准备的。只有当上述3个条件都满足了,动态类型才能发挥其威力,即很好的支持虚函数动态绑定机制,为什么呢?这与C++编译器对多态继承结构中的成员函数调用机制有关。
        假设有以下继承结构:class Drived : public Base,然后有表达式p->mem()或obj.mem(),其中mem()是一个类成员函数,但具体是基类还是子类,现在可以不用管,而且我们也不用管p或者obj是指向哪个类。当程序的编译期,当编译器遇到表达式p->mem()或obj.mem(),执行以下步骤:
       (1)首先确定p(或obj)的静态类型,即声明类型;
       (2)然后在p(或obj)的静态类型中查找mem()函数,如果没有找到,按照作用域规则,这时编译器会到其直接基类中寻找,并依次向上,直到达到继承链的顶端,如果在这个过程中找到了,则继续下一步,如果没有找到,则报错;
       (3)一旦找到名字匹配的mem()函数,则编译器会根据mem()是否是虚函数,以及我们是通过指针或引用(如p)来调用mem()函数,还是通过类对象(如obj)来调用mem()函数,生成不同的代码。如果mem()是虚函数且是通过指针或引用调用(如p->mem()),编译器生成的代码会检查其动态类型,这就是动态绑定的过程,也就是说编译器生成的代码会直到运行期确定了动态类型才决定具体调用基类还是子类的虚函数mem();如果mem()不是虚函数且是通过对象调用(如obj.mem()),则编译器会产生一个常规的函数调用代码,可以直接确定调用哪一个mem()函数。

动态绑定

        使 用 相 同 的 代 码 处 理 不 同 类 型 的 对 象 ; \color{#FF3030}{使用相同的代码处理不同类型的对象;} 使使用基类的指针或者引用调用一个虚成员函数时会执行动态绑定。由于直到运行时才知道到底调用了哪个版本的虚函数,因此虚函数必须被定义。

C++的多态

       我们把具有继承关系的多个类型称为多态类型,因为我们能使用这些类型的“多种形式”而无需在意他们的差异。引用或指针的静态类型与动态类型不同这一事实正式C++语言支持多态性的根本所在。
       当我们使用基类的引用或指针调用基类中定义的一个函数时,我们并不知道该函数的真正作用的对象时什么类型,因为他可能是一个基类的对象,也可能是一个派生类的对象。如果该函数是虚函数,则直到运行时才会决定到底执行哪个版本,判断的依据是引用或者指针所绑定的对象的真实类型。
       另一方面,对非虚函数的调用在编译时进行绑定,通过对象进行的函数调用也在编译时进行绑定。对象的类型是确定不变的,我们无论如何都不可能令对象的动态类型与静态类型不一致。因此通过对象进行的函数调用将在编译时绑定到该对象所属类中的函数版本上。

  • 11
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
C++中,静态类动态类型的概念和区别与其他编程语言似。 静态类是指在编译期就可以确定的变量类型,变量的类型在编译时就已经确定,编译器会对变量类型进行检查,如果类型不匹配会报错。C++中的变量类型可以使用关键字来指定,例如int、float、double等。 动态类型是指在运行期才能确定的变量类型,变量的类型在编译时不会进行检查,而是在运行时根据实际情况来确定变量类型C++中通过使用指针或引用来实现动态类型,可以将指向派生的指针或引用赋值给指向基的指针或引用,实现多态。 下面是一个使用静态类动态类型的例子: ``` #include <iostream> using namespace std; class A { public: void func() { cout << "A::func()" << endl; } }; class B : public A { public: void func() { cout << "B::func()" << endl; } }; int main() { A a; // 静态类为A B b; // 静态类为B A* pa = &a; // 静态类为A,动态类型为A pa->func(); // 输出 "A::func()" pa = &b; // 静态类为A,动态类型为B pa->func(); // 输出 "B::func()" return 0; } ``` 在上面的代码中,A和B是两个,B是从A派生而来的。在main函数中,首先定义了一个静态类为A的对象a和一个静态类为B的对象b。然后定义了一个指向A的指针pa,并将其指向a,此时pa的动态类型也是A。调用pa的func函数,输出"A::func()"。 接着将pa指向b,此时pa的动态类型变为B。再次调用pa的func函数,输出"B::func()"。可以看到,通过使用动态类型,可以实现不同对象的动态绑定,实现多态。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值