编写无警告的代码

本文内容版权归原作者所有,我不对本文内容的真实性做任何保证。
原文地址:编写无警告的代码

今天把项目的Qt版本从Qt4.6.3升级到Qt4.8.4,重新编译项目代码的时候,特别关注了一下编译器的警告。于是找到《C++编程规范——101条规则、准则与最佳实践》翻了翻,重温了一下第1条 在高级别警告干净利落地进行编译。
如果编译器对某个构造发出警告,一般表明代码中存有潜在的问题。警告就好比代码的“肿瘤”,可能是良性的也可能恶性的——作为代码医生的我们不能对其视而不见。必须“把它弄清楚”,然后通过“改写代码以排除警告”!
典型的编译器警告示例:
1.”Unused function parameter”(未使用的函数参数):
编译器会对没有使用到的函数参数报告警告。如果确实不需要,那直接删除函数参数名就行了。
2. “Variable defined but never used”(定义了从未使用过的变量):
如果确实不需要,可以直接将之删除。 如果该变量的实例化是为了启动某项服务,则经常可以通过插入一个变量本身的求值表达式,使编译器不再报警。(这种求值不会影响运行时的速度)
3. “Variable may be used without being initialized”(变量使用前可能未经初始化):
这种警告通常意味着你的代码存在问题,请慎重处理之。
4. “Missing return”(遗漏了return语句)。:
这可能是一种好现象。即使你认为控制永远都不会运行到结尾或某个分支,则应该加上执行assert(false ) 的语句。
像下面这样(兼有注释作用的断言):

  default:  
     assert( !"should never get here!" );  // !"string" 的求值结果为false  
  
  1. “signed/unsigned mismatch”(有符号数/无符号数不匹配):
    通常需要插入一个显式的强制转换。
  2. “warning: type qualifiers ignored on function return type”(忽略了函数返回类型的类型限定符):
    通常是因为在函数前面的返回值面前添加了多余的 const 限定符。
    例如,const int getAge() const { return m_age; } ,最前面的const 是多余的,编译器会报告警告。
  3. “warning: deprecated conversion from string constant to ‘char*'”(不推荐的转化用法):
    函数原型:void setName(char *name);

    函数调用:setName(“personName”); // 编译器报告警告

释义:这是因为 char * 是一个指针,其背后的含义是:给我一个字符串,我可以修改它。

而如果我们传给函数一个“字符串常量”,这应该是没法被修改的。所以说,比较合理的办法是把参数类型修改为const char * ,而这个类型背后的含义是:给我一个字符串,我只要读取它(const意味着只读)。
8. “warning: “MAX_PATH” redefined” (重复定义) :

  #ifndef MAX_PATH
  #define MAX_PATH(260)
  #endif
  
  1. 第三方头文件。

  2. “auto-importing has been activated without –enable-auto-import specified on the command line”

在WPF中无操作后返回

在很多时候,我们需要使用WPF来实现一些程序,例如展示程序。最近我就做了这样的程序。但是有一个问题,就是如果做展示程序(类似触摸屏的那种),那么有必要让程序在无操作后返回到一个类似屏保的界面(可以参考windows的屏幕保护或者自动锁定)。很遗憾,我没有在wpf中发现具有这样功能的类/函数,只能转向求助于windows API。

在windows api中(“user32.dll”),有一个名为“ GetLastInputInfo”的API,在MSDN上,它的解释是这样的:

This fundtion is useful for input idle detection. However, GetLastInputInfo does not provide system-wide user input information across all running sessions. Rather, GetLastInputInfo provides session-specific user input information for only the session thar invoked the function.

The tick count when the last input event was received (see LastInputInfo) is not guaranteed to be incremental. In some cases, the value might be less than the tick count of a prior event. For example, this can be caused by a timing gap between the raw input thread and the desktop thread or an event raised by SendInput, which supplies its own tick count.

上面第一段话的意思是,GetLastInputInfo并不提供全局(在整个桌面环境中)的输入信息,而是提供特定的会话的输入信息。

第二段的意思是,接收到的输入时间的tick count并不是完全准确的,在某些情况下,可能会比实际的值要小,这种情况可能会由纯输入线程和桌面线程之间的时间差,或者是由SendInput引起。

_注:由于个人水平有限,以上引语的翻译可能不准确,所以最好阅读原文。另外,以上内容不会随着其出处更新而更新,用时请参考其出处_

所以最终我选用这个GetLastInputInfo,在C#/WPF中写好引用即可。
下面是本功能的核心代码

using System.Runtime.InteropServices; // 如果需要调用外部的dll的话,这个引用是必要的
using System.Windows;
using System.Windows.Threading;  // 此处是为了使用WPF的DispatcherTimer,
                                                                 // 如果你需要使用其他的Timer,请自行using
namespace
{
    public class TestClass
    {
        private DispatcherTimer timer; // 我们需要使用的timer,在timer里面我们要检测上一次操作的信息
        private readonly static int defultIdleAllowed = 5; // 默认的允许idle时间(单位为s),在这个时间里,如果没有操作,那么就返回,或者执行其他的操作
        private static int currentIdleTimeRemained = defaultIdleAllowed; // 当前剩余的可以idle的时间,单位为s

        public TestClass()
        {
            timer = new DispatcherTimer();
            timer.Tick += new EventHandler(Timer_Tick);
            timer.Interval = new TimeSpan(0, 0, 1);
            timer.Start();
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            if(HasActionInLastSecond())
            {
                if(--currentIdleTimeRemained == 0)
                {
                    currentIdleTime = defaultIdleAllowed;
                    timer.Stop();

                    // 在这里你可以做一些类似返回主屏幕的操作

                    timer.Start();
                }
            }
            else
            {
                currentIdleTime = defaultIdleAllowed;
            }
        }

        /// <summary>
        /// 未操作时间超过1s才开始计数
        /// </summary>
        /// <returns>bool,表示未操作时间是否超过1s</returns>
        private bool HasActionInLastSecond()
        {
            return GetIdleTime() / 1000 > 1;
        }

        /// <summary>
        /// 通过window api获得上次键鼠操作的时间,返回值为ms
        /// </summary>
        /// <param name = "pLastInputInfo">ref, 存放时间的一个结构体,在下文中会有定义</param>
        /// <returns></returns>
        [DllImport("user32.dll")]
        static extern bool GetLastInputInfo(ref PLASTINPUTINFO pLastInputInfo);

        private static long GetIdleTime()
        {
            PLASTINPUTINFO pLastInputInfo = new PLASTINPUTINFO;
            pLastInputInfo.cbSize = Marshal.SizeOf(pLastInputInfo);
            if(!GetLastInputInfo(ref pLastInputInfo))
            {
                return 0;
            }
            return Environment.TickCount- pLastInputInfo.dwTime;
        }

        // PLASTINPUTINFO结构体定义
        [StructLayout(LayoutKind.Sequential)]
        private struct PLASTINPUTINFO
        {
            [MarshalAs(UnmanagedType.U4)]
            public int cbSize;
            [MarshalAs(UnmanagedType.U4)]
            public uint dwTime;
        }