静态类型和动态类型检查

1、静态类型和动态类型

如果大家希望学好C++的话那请务必多学习一下c++内部的对象模型,前面有很多博客都是关于这方面的不过都不是自己写的(因为自己很懒)。

举个例子:类A,类B,如果B没有继承A那这就没什么关系了,不过我这里主要说的就是说它们有关系,所以我这里就假如B继承了A。那B将继承A的某些特性(比如成员函数,成员变量等等)。那假如没有什么虚函数什么的,那这种关系如下:

         B b;

而如果有虚函数的话(A有,B无)(A无,B有)(A有,B有并重写)模型如下。

                                 B b

这里我就给出最简单的图,详细的可找相关资料看下,该图说明一下,只要A或者B类有虚函数那编译器就会为他生成vtable和type_info其中都是为了动态检查用的。vtable是虚函数用的,而type_info为了支持RTTI用的。

 

那我们看重点静态类型和动态类型

 静态多态性:函数多态性——函数重载

                           模板多态性——C++模板(类模板、函数模板)

 动态多态性:虚函数(只有用地址才能实现动态多态性)

                         

只有采用“指针->函数()或“引用变量.函数()的方式调用C++类中的虚函数才会执行动态绑定。对于C++中的非虚函数,因为其不具备动态绑定的特征,所以不管采用什么样的方式调用,都不会执行动态绑定。

 

         

代码形式

对于虚函数

对于非虚函数

作用

绑定方式

作用

绑定方式

类名::函数()

调用指定类的指定函数

静态绑定

调用指定类的指定函数

静态绑定

对象名.函数()

调用指定对象的指定函数

静态绑定

调用指定对象的指定函数

静态绑定

引用变量.函数()

调用被引用对象所属类的指定函数

动态绑定

调用引用变量所属类的指定函数

静态绑定

指针->函数()

调用被引用对象所属类的指定函数

动态绑定

调用指针变量所属类的指定函数

静态绑定

注:被引用对象所属类 是 指针 或 引用 指向的对象的实际类型;

    引用变量所属类、指针变量所属类 是 定义 引用变量、指针变量的类型;

    以上两种类型可能相同,也可能不同。

下面看一个例子:

 

  1. <span style="font-size:18px">#include <iostream>  
  2.   
  3. using namespace std;  
  4.   
  5. class A  
  6. {  
  7. public:  
  8.     void fun()  
  9.     {  
  10.         cout<<"A::fun"<<endl;  
  11.     }  
  12.     void fun2()  
  13.     {  
  14.         cout<<"A::fun2"<<endl;  
  15.     }  
  16. };  
  17. class B:public A  
  18. {  
  19. public:  
  20.     void fun()  
  21.     {  
  22.         cout<<"B::fun"<<endl;  
  23.     }  
  24.     void fun3()  
  25.     {  
  26.         cout<<"B::fun3()"<<endl;  
  27.     }  
  28. };  
  29. void main()  
  30. {  
  31.     A a;  
  32.     a.fun();//静态检查  
  33.     B b;  
  34.     b.fun();//静态检查  
  35.     b.fun2();//继承会将函数也继承  
  36.     A *x = &b;  
  37.     x->fun();  
  38.     x->fun2();  
  39.     x->fun3();  
  40. }  
  41.   
  42. </span>  

上面解释一下,第一句和第三句以为都是对象所以进行静态检查所以调用对应的自己函数,而第五句因为B继承了A的成员函数所以也可以调用(但此时要注意调用权限这里为public),到第六句出现了指针所以后三句都将进行动态检查但由于该程序没有虚函数所以不会进行动态绑定。首先编译器知道x静态为A类所以x->fun();即为A::fun,同理fun2(),但最后一句就会出现错误了,因为fun3()不是父类的就相当于父类无法继承子类。

那如果程序改一下,将A类中fun()声明为虚函数。B类fun3()声明为虚函数,那执行过程将是x静态为A*,不过编译器有type_info所以知道指向的是B对象,因此执行虚函数fun的时候将通过vptr绑定到B的fun,而fun2不是虚函数也就不会去用vptr到vtable里找执行函数所以不会动态绑定。 

 所以我们也就知道动态绑定需要额外的时间开销的。

 另外如果B中fun3为虚函数那x->fun3()是否成功呢?答案是不可以的,因为首先x是A*此时A中并没有fun3的类型说明所以就报错说A中没有该成员函数。

那我们就可以总结下:c++实现并没有将对虚函数的调用采用运行时进行而是派生类定义中的名字(对象或函数名)将义无反顾地遮蔽(即隐藏)掉基类中任何同名的对象或函数(跟普通函数一样的),只是说虚函数的调用会进行转换(通过vptr到vtable里面查找),而普通函数只进行简单的调用。

上面简单来说就是说比如父类指针指向子类对象,那首先指针进行静态检查时A*的,然后看看调用的函数是虚函数还是普通成员函数,如果是虚函数那该指针通过vptr到vtable里面找改函数对应得函数指针如果该函数被子类修改过了那该槽被改写了那将调用子类的该函数,而如果没有被修改也就是说该槽没有被重写那就直接调用父类自己的该虚函数。第二,如果该函数是父类的普通成员函数的话那直接进行调用不管子类有没重写过,因为进行静态检查该指针只是父类型的。

所以最后说一句:虚函数调用语句的参数类型检查并没有采用推迟到运行时进行的。其实一开始就已经静态检查好了,只是调用的时候进行动态寻址而已。

再说一句,呵呵:为什么父类要是虚析构函数。我想应该清楚了吧!

因为首先怕出现这样的情况:A* a  = new B();

那么当delete a;的时候因为a静态检查时A*那如果不是虚函数的直接调用~a了。因为vtable的槽没有被子类改写所以会出现无法调用~B()。而假如写了呢那将出现就相当于~B()改写了~A()对应的槽变为了~B(),所有delete a先调用~B()然后调用父类~A()(这个调用子类析构函数然后调用父类析构函数是编译器规定的)。怎么样大家清楚了吧~

<think>好的,用户现在想了解静态类型动态类型的定义。之前他们问了动态绑定,现在转向类型系统的基础概念,可能是在逐步深入面向对象或编程语言特性。我需要先明确这两个术语的定义,然后比较它们的区别特点。 首先,静态类型是指在编译时确定变量类型,类型检查在编译阶段完成,比如Java、C++。动态类型则是在运行时确定,类型与值关联,如Python、JavaScript。需要解释清楚两者的核心差异,比如类型检查时机、错误捕获时间、灵活性等。 用户可能想知道为什么有的语言用静态,有的用动态,各自的优缺点。可能需要举例子说明,比如静态类型在编译时发现类型错误,而动态类型更灵活但运行时可能出错。还要注意他们之前的问题涉及动态绑定,所以可能类型系统与多态有关联,可以稍微提一下,但重点在定义。 要确保回答结构清晰,分点列出定义、特点、区别、例子,避免使用Markdown,用自然的中文。还要检查是否需要补充其他方面,比如强类型弱类型是否相关,但用户只问了静态与动态,可能不需要展开。需要保持回答准确且易于理解,帮助用户建立基本概念。</think>以下是静态类型(Static Typing)与动态类型(Dynamic Typing)的清晰定义对比分析: --- ### **1. 静态类型(Static Typing)** - **定义**: 变量在**声明时即确定类型**,且类型在程序运行前(编译时)必须完全确定。类型检查发生在编译阶段,违反类型规则会直接报错,无法生成可执行文件。 - **核心特点**: - **类型绑定时机**:变量类型在代码编写时(编译前)明确声明。 - **错误发现时机**:类型错误(如字符串与数字相加)在编译阶段被捕获。 - **灵活性**:类型严格,需显式声明或转换类型。 - **性能**:编译时类型已知,可优化内存分配计算逻辑,运行效率高。 - **典型语言**: Java、C、C++、Go、TypeScript(需编译的强静态类型语言)。 - **示例**(Java): ```java int num = 10; // 变量num类型明确为整型 String s = "Hello"; num = "20"; // 编译报错:类型不匹配 ``` --- ### **2. 动态类型(Dynamic Typing)** - **定义**: 变量的类型在**运行时动态确定**,且类型可以随赋值内容改变。类型检查推迟到程序运行阶段,类型错误在运行时才会触发异常。 - **核心特点**: - **类型绑定时机**:类型与值绑定,而非变量。变量可以随时指向任意类型的值。 - **错误发现时机**:类型错误(如调用不存在的方法)在运行到相关代码时才会暴露。 - **灵活性**:无需显式声明类型,适合快速开发原型设计。 - **性能**:运行时需动态解析类型,效率通常低于静态类型语言。 - **典型语言**: Python、JavaScript、Ruby、PHP。 - **示例**(Python): ```python x = 10 # x此时是整型 x = "Hello" # 运行时x变为字符串类型 x + 20 # 运行时报错:字符串与数字不能相加 ``` --- ### **3. 核心区别总结** | **特性** | **静态类型** | **动态类型** | |------------------|----------------------------------|----------------------------------| | **类型确定时机** | 编译时 | 运行时 | | **类型声明** | 变量需显式声明类型 | 变量无需声明类型 | | **错误检查** | 编译阶段发现类型错误 | 运行阶段触发类型错误 | | **灵活性** | 低(类型固定,需强制转换) | 高(类型可动态变化) | | **性能** | 高(编译时优化) | 低(运行时解析) | | **适用场景** | 大型项目、高可靠性需求 | 快速开发、脚本小型项目 | --- ### **4. 补充说明** - **强类型 vs 弱类型**: 静态/动态类型关注“何时检查类型”,而强/弱类型关注“类型转换是否严格”。 - 强类型(如Python):不允许隐式类型转换(如`"10" + 10`报错)。 - 弱类型(如JavaScript):允许隐式类型转换(如`"10" + 10`结果为`"1010"`)。 - **趋势与融合**: 现代语言(如TypeScript、Python的类型注解)通过“渐进式类型系统”结合二者优势,允许开发者选择静态类型检查以提高安全性,同时保留动态类型的灵活性。 --- ### **5. 实际选择建议** - **选静态类型**:需要高可靠性、团队协作或性能敏感场景(如系统底层开发)。 - **选动态类型**:快速迭代、脚本工具或小型项目(如数据分析、Web原型开发)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值