• 在C++中通过模板规避潜在错误


    模板(template)为C++带来了泛型编程的能力,但也带来了使用难度。大体上,使用模板的三大动机分别是提高复用性、去除强制转换和规避潜在错误。在此让我们看一看规避错误的一个例子。

    假设我们有图 1所示的被简化了的定时器管理模块程序。从构造函数来看,它的三个参数分别指明了定时器的延时时间、回调函数和回调函数的参数,其中的回调函数是通过timer_callback_t类加以封装的。当定时器到期时,它的fire()函数会被调用。间接地,fire()函数调用定时器所保存回调函数类对象的handle()函数。

    1. class timer_callback_t 
    2.     virtual void handle (timer_t &_timer, timer_callback_arg_t *_p_arg) = 0; 
    3. }; 
    4.  
    5. class timer_t 
    6. public: 
    7.     timer_t (msecond_t _duration, timer_callback_t *_p_callback,  
    8.         timer_callback_arg_t *_p_callback_arg); 
    9.  
    10. private: 
    11.     void fire () 
    12.     { 
    13.         p_callback_.handle (this, p_callback_arg_); 
    14.     } 
    15.  
    16.     timer_callback_t *p_callback_; 
    17.     timer_callback_arg_t *p_callback_arg_; 
    18. }; 
    图1
     
    图 2示例了如何使用定时器。首先,得针对定时器的用途通过派生timer_callback_t类实现相应的回调函数类。接着,在创建定时器时需实例化回调函数类。图中foo()和bar()函数分别示例了两种实例化回调函数类的方法,前者采用的是定义静态类变量,后者采用的是通过new进行动态分配。
    1. class connect_timeout_callback_t: public timer_callback_t 
    2.     void handle (timer_t &_timer, timer_callback_arg_t *_p_arg) 
    3.     { 
    4.         // do something here 
    5.     } 
    6. }; 
    7.  
    8. void foo () 
    9.     static connect_timeout_callback_t callback; 
    10.     timer_t *p_timer = new timer_t (100, &callback, 0); 
    11.  
    12. void bar () 
    13.     connect_timeout_callback_t *p_callback = new connect_timeout_callback_t (); 
    14.     timer_t *p_timer = new timer_t (100, p_callback, 0); 
    图2
     
    定时器模块的实现使得在foo()和bar()函数中实例化回调函数类的方法需要注意一些点,否则容易犯错。在foo()函数所使用的方法中,如果不小心忘记了将类变量定义成静态的,会因为变量分配在栈上而最终导致程序出错;在bar()函数中,如果忘记了将通过new分配获得的内存用delete释放,则会产生内存泄漏。能否通过设计避免这些潜在的问题呢?
     
    图3是对定时器管理模块采用模板重写后的程序。其中最大的变化是timer_t类的构造函数省去了指定回调函数类实例,且回调函数类和回调函数参数成为了两个模板类型。另一个变化是,fire()函数中通过定义静态变量的方式实例化回调函数类。
    1. template <typename T_CALLBACK, typename T_CALLBACK_ARG> 
    2.     class timer_callback_t 
    3.     virtual void handle (timer_t <T_CALLBACK, T_CALLBACK_ARG> &_timer, 
    4.         T_CALLBACK_ARG _arg) = 0; 
    5. }; 
    6.  
    7. template <typename T_CALLBACK, typename T_CALLBACK_ARG> 
    8.     class timer_t 
    9. public: 
    10.     timer_t (msecond_t _duration, T_CALLBACK_ARG _callback_arg); 
    11.  
    12. private: 
    13.     void fire () 
    14.     { 
    15.         static T_CALLBACK callback; 
    16.         callback.handle (*this, callback_arg_); 
    17.     } 
    18.  
    19.     T_CALLBACK_ARG callback_arg_; 
    20. }; 
    图3
     
    图4示例说明了新实现下如何使用一个定时器。很显然,我们通过模板将一些潜在问题通过内部化的方式给规避了。
      1. class connect_timeout_callback_t: 
      2.     public timer_callback_t <connect_timeout_callback_t, void *> 
      3.     void handle (timer_t <connect_timeout_callback_t, void *> &_timer, void *_arg) 
      4.     { 
      5.         // do something here 
      6.     } 
      7. }; 
      8.  
      9. void foo () 
      10.     timer_t <connect_timeout_callback_t, void *> *p_timer = 
      11. new timer_t < connect_timeout_callback_t, void *> (100, 0); 
  • 相关阅读:
    可能不知道的C#特性
    设计模式の依赖注入
    How to find WWN and WWPN of HBA card in Linux
    fio IO测试工具
    centos/redhat 多路径存储使用 客户端
    centos/redhat 系统误删除逻辑卷之后如何恢复
    How to use lspci, lsscsi, lsusb, and lsblk to get Linux system devices information
    How to Check and Repair EXT4 Filesystem in Linux
    如何在 Linux 上扫描/检测新的 LUN 和 SCSI 磁盘
    小程序开发知识点总结归纳
  • 原文地址:https://www.cnblogs.com/xiangcunjiaoshi/p/12598294.html
Copyright © 2020-2023  润新知