• C++对象模型(20)-- 函数语义学:函数和变量的绑定问题


    1、静态类型和动态类型

    静态类型:对象定义时的类型,编译期间就确定好的。定义的时候是什么就是什么。

    动态类型:对象目前所指向的类型,运行时才确定的类型。一般只有指针和引用才有动态类型。

    比如下面的代码:

    1. class Base {
    2. };
    3. class Derive : public Base {
    4. };
    5. class Derive2 : public Base {
    6. };
    7. int main()
    8. {
    9. Base base;
    10. Base* pb;
    11. Base* pb1 = new Derive();
    12. Base* pb2 = new Derive2();
    13. }

    (1)静态类型:

    base对象的静态类型是Base;

    pb指针的静态类型是Base*;

    pb1指针的静态类型是Base*;

    pb2指针的静态类型是Base*。

    (2)动态类型:

    pb1指针的动态类型是Derive。

    pb2指针的动态类型是Derive2。

    动态类型在运行期间可以改变。比如:

    1. pb = pb1;
    2. pb = pb2;

    2、静态绑定、动态绑定

    静态绑定:绑定的是静态类型,所对应的函数或属性依赖于对象的静态类型,发生在编译期间。

    动态绑定:绑定的是动态类型,所对应的函数或属性依赖于对象的动态类型,发生在运行期间。

    C++继承中的多态就是通过动态绑定实现的。要实现多态,必须存在虚函数且调用虚函数,没有虚函数就不可能存在多态。

    (1)继承非虚函数

    1. class Base {
    2. public:
    3. void func() {
    4. cout << "Base::func()" << endl;
    5. }
    6. };
    7. class Derive : public Base {
    8. public:
    9. void func() {
    10. cout << "Derive::func()" << endl;
    11. }
    12. };
    13. int main()
    14. {
    15. Derive derive;
    16. Base* pb = &derive;
    17. pb->func();
    18. }

    这里的Base类指针pb指向了Derive类对象,通过指针pb调用func()函数,最终执行的是哪个类的func()函数呢?

    从执行结果看,最终执行的是Base类的func()函数。

    因为普通成员函数是静态绑定,类指针pb的静态类型是Base,所以调用的是Base类的func()函数。

    所以,在函数重写(override)时,一定要把函数定义成virtual,否则不会产生多态效果。

    (2)重新定义虚函数的参数的默认值

    看下面这个例子:

    1. class Base {
    2. public:
    3. virtual void func(int i = 1) {
    4. cout << " Base::func() i = "<< i << endl;
    5. }
    6. };
    7. class Derive : public Base {
    8. public:
    9. virtual void func(int i = 2) {
    10. cout << " Derive::func() i = "<< i << endl;
    11. }
    12. };
    13. int main()
    14. {
    15. Derive derive;
    16. Base* pb = &derive;
    17. pb->func();
    18. }

    程序执行输出的结果如下:

    Derive类的func()函数,形参i的默认值是2,这里却输出1。

    因为函数参数的默认值是静态绑定的,指针pb的静态类型是Base,所以函数参数的默认值是Base类的。

    3、数据成员绑定时机

    (1)成员函数参数类型的确定时机

    1. typedef string MY_TYPE;
    2. class Base {
    3. public:
    4. void func(MY_TYPE _type) {
    5. this->type = _type;
    6. }
    7. private:
    8. typedef int MY_TYPE;
    9. MY_TYPE type;
    10. };

    这段代码编译后会报错,this->type = _type; 这行有错误。_type是string类型,this->type是int类型,类型不匹配。

    从这个例子我们可以看到,成员函数的参数类型是在编译器最近一次碰到时决定的:

    在func()函数之前,MY_TYPE是string类型;在变量type之前,MY_TYPE是int。

    (2)成员函数的解析时机

    1. string type;
    2. class Base {
    3. public:
    4. void func() {
    5. cout << typeid(type).name() << endl;
    6. }
    7. private:
    8. int type;
    9. };
    10. int main()
    11. {
    12. Base base;
    13. base.func();
    14. return 0;
    15. }

    程序执行后,发现打印出来的type类型是int,不是全局定义的string。

    这里,我们要记住:对成员函数func的解析,是在整个类定义完后才开始的。在整个类定义完后,编译器看到了成员变量的定义:int type,所以type变量是int型,而不是全局定义的string型。

  • 相关阅读:
    Mathorcup数学建模竞赛第四届-【妈妈杯】C题:家庭暑假旅游套餐的设计(附MATLAB和SAS代码)
    LeetCode 0234. 回文链表
    C++基础入门 运算符
    mysql存储过程
    自己动手实现rpc框架(二) 实现集群间rpc通信
    重学JavaSE 第17章 : 反射机制、Class类、类加载器、静态代理、动态代理
    驱动开发--自动创建节点udev机制的实现过程分析
    vue-element-admin 综合开发五:引入 echarts,封装echarts 组件
    企业密码管理器
    Day07--wxs的概念以及其基本的用法
  • 原文地址:https://blog.csdn.net/mars21/article/details/133984902