• 想让 c++ 的 static 成员具有多态性


    我也没想到,有一天我会有这么奇怪的想法,会想让 c++ 的 static 成员具有多态性。需求的产生是这样的:

    1. 我需要在页面上产生一系列的控件,就叫 widget 吧。这些控件除了在点击时具有不同的表现之外,其他行为都是相同,因此我考虑建立一个基类,用来控制这些相同的属性或行为。
    2. 使用的这套 API,提供了对点击事件的监听,需要我自己设置静态回调方法,但是没有办法向这个回调方法传参
    3. 我需要控件在点击时具有不同表现,只能在回调方法里做文章,简单说,就是根据控件名称做出不同响应

    上面的需求看起来蛮容易的,但操作起来却有不少坑,先看看我产生问题的第一版代码吧。

    
    /*
    * base Widget
    */
    class Widget
    {
    private:
        //1. 控件名,每个子类名称不同。
        std::string mName;
    public:
        /*
        * 回调方法
        *
        * 2. API 要求,这个 callback 方法只能是静态的;
        * 3. 在 callback 里,希望根据不同的组件名,作出不同响应;
        *    在 callback 里使用 mName,也就是想在静态方法里使用非静态成员变量
        */
        static int callback(int param) {
            doWithName(param);
        }
    
        /*
        * addCallback 是 API 提供的接口,用于给组件添加点击事件回调方法
        *
        * 4. 这个接口,并没有传递参数的地方,所以 callback 虽然设计了 param,
        *    但我并没有办法传过去。
        */
        /*
        void addCallback(Widget widget, int eventType, int(*callback)(int));
        */
    }
    
    /*
    * child widget 1
    */
    class FirstWidget : public Widget
    {
        
    }
    
    /*
    * child widget 2
    */
    class SecondWidget : public Widget
    {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47

    在这一版代码里,我希望 mName 能表现出多态性,因此未将其设为静态变量。然后尴尬的发现在静态方法 callback 方法中无法使用,且没法通过参数传递将其传入。那么我就只能把 mName 设成静态的了,在这一刻,我多么希望静态成员在子类中能有独立地址,从而表现出多态性,然而只能想想。
    目前我的解决方法是在每个子类中都有自己的静态变量 mName,并定义自己的 callback 方法,在基类中新增一个函数指针来指向每个子类的 callback 方法,实现多态性。如下:

    
    /*
    * base Widget
    */
    class Widget
    {
    private:
        //定义函数指针,指向回调函数
        int(*callbackPtr)(int);
    
    public:
        Widget(int(*callback)(int)) {
            callbackPtr = callback;
        }
    
        /* call addCallback */
        addCallBack(widget, event, callbackPtr)
    
        /*
        void addCallback(Widget widget, int eventType, int(*callback)(int));
        */
    }
    
    /*
    * child widget 1
    */
    class FirstWidget : public Widget
    {
    private:
        //在类中定义 mName
        static std::string mName;
        
        //定义 callback 方法
        static int mCallback(int param) {
            /* do somthing according to mName*/
        }
    
        //在子类构造时,将回调方法作为参数传到基类
        FirstWidget() : Widget(mCallback) {};
    }
    
    /*
    * child widget 2
    */
    class SecondWidget : public Widget
    {
    private:
        //在类中定义 mName
        static std::string mName;
        
        //定义 callback 方法
        static int mCallback(int param) {
            /* do somthing according to mName*/
        }
    
        //在子类构造时,将回调方法作为参数传到基类
        SecondWidget() : Widget(mCallback) {};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    然而,我还是希望:不用在每个子类中都写一次 callback 方法,mName 成员既能在基类的 callback 方法里被使用,又能表现出多态性。求大佬指点一波。

  • 相关阅读:
    ROS2之spin()函数解析(C++版)
    【linux命令讲解大全】106.使用eject命令退出抽取式设备的方法和选项
    review第1遍,git版本控制,项目总结,220629,md+本地视频,
    解析一下vue项目、scoped、ref属性、props其它、mixin、插件、elementui
    入门深度学习—从配置python到网络模型
    Kafka学习总结
    Ubuntu Linux 操作系统-清华大学开源软件镜像站下载
    【SQLite】三、SQLite 的常用语法
    ESP32_esp-adf环境搭建
    信息标准化介绍
  • 原文地址:https://blog.csdn.net/hejnhong/article/details/127642606