COleDateTime::SetDateTime和noexcept(代码分析)

COleDateTime::SetDateTime and noexcept (code analysis)

查看此代码:

COleDateTime CSpecialEventDlg::GetSpecialEventDate() noexcept
{
    COleDateTime    datEvent;

    if (datEvent.SetDateTime(m_datEvent.GetYear(),
            m_datEvent.GetMonth(),
            m_datEvent.GetDay(),
            0, 0, 0) != 0)
    {
        // Error

    }

    return datEvent;
}

我的代码分析表明我可以添加 noexcept(我确实这样做了)。但我决定再调查一下。我注意到 SetDateTime returns 一个值:

Zero if the value of this COleDateTime object was set successfully; otherwise, 1. This return value is based on the DateTimeStatus enumerated type. For more information, see the SetStatus member function.

对于后一个函数 (SetStatus),它指出:

The status parameter value is defined by the DateTimeStatus enumerated type, which is defined within the COleDateTime class. See COleDateTime::GetStatus for details.

现在,GetStatus 的文档记录了以下定义:

DateTimeStatus GetStatus() const throw();

因此,如果有错误,它会抛出异常。因此,我决定查看 SetDateTime:

的 MFC 源代码
inline int COleDateTime::SetDateTime(
    _In_ int nYear,
    _In_ int nMonth,
    _In_ int nDay,
    _In_ int nHour,
    _In_ int nMin,
    _In_ int nSec) throw()
{
    SYSTEMTIME st;
    ::ZeroMemory(&st, sizeof(SYSTEMTIME));

    st.wYear = WORD(nYear);
    st.wMonth = WORD(nMonth);
    st.wDay = WORD(nDay);
    st.wHour = WORD(nHour);
    st.wMinute = WORD(nMin);
    st.wSecond = WORD(nSec);

    m_status = ConvertSystemTimeToVariantTime(st) ? valid : invalid;
    return m_status;
}

它使用ConvertSystemTimeToVariantTime设置状态。 使用:

inline BOOL COleDateTime::ConvertSystemTimeToVariantTime(_In_ const SYSTEMTIME& systimeSrc)
{
    return AtlConvertSystemTimeToVariantTime(systimeSrc,&m_dt);
}

并且那个使用:

inline BOOL AtlConvertSystemTimeToVariantTime(
    _In_ const SYSTEMTIME& systimeSrc,
    _Out_ double* pVarDtTm)
{
    ATLENSURE(pVarDtTm!=NULL);
    //Convert using ::SystemTimeToVariantTime and store the result in pVarDtTm then
    //convert variant time back to system time and compare to original system time.
    BOOL ok = ::SystemTimeToVariantTime(const_cast<SYSTEMTIME*>(&systimeSrc), pVarDtTm);
    SYSTEMTIME sysTime;
    ::ZeroMemory(&sysTime, sizeof(SYSTEMTIME));

    ok = ok && ::VariantTimeToSystemTime(*pVarDtTm, &sysTime);
    ok = ok && (systimeSrc.wYear == sysTime.wYear &&
            systimeSrc.wMonth == sysTime.wMonth &&
            systimeSrc.wDay == sysTime.wDay &&
            systimeSrc.wHour == sysTime.wHour &&
            systimeSrc.wMinute == sysTime.wMinute &&
            systimeSrc.wSecond == sysTime.wSecond);

    return ok;
}

此时我迷路了。简而言之,我假设 SetDateTime 不会 抛出异常。


我明白这一点,如果我决定在 if 子句中调用 GetStatus 那么我们确实有可能抛出异常。

您显示源代码的所有三个 MFC 函数(COleDateTime::SetDateTimeCOleDateTime::ConvertSystemTimeToVariantTimeAtlConvertSystemTimeToVariantTime)根据它们的声明都有可能抛出异常(因为他们没有相反的规范,例如 noexcept).

然而,这并不意味着他们会(或什至可能会)。进一步深入研究 MFC 源代码,在这三个函数中,我唯一能看到的可能引发异常的地方是 ATLENSURE(pVarDtTm!=NULL); 行(在第三个函数中)。

ATLENSURE宏定义如下:

#define ATLENSURE(expr) ATLENSURE_THROW(expr, E_FAIL)

ATLENSURE_THROW 又是这样定义的:

#define ATLENSURE_THROW(expr, hr)          \
do {                                       \
    int __atl_condVal=!!(expr);            \
    ATLASSUME(__atl_condVal);              \
    if(!(__atl_condVal)) AtlThrow(hr);     \
} __pragma(warning(suppress:4127)) while (0)

因此,在您的代码中,如果 expr(在上面的代码片段中)为 null(双爆炸,!! 伪运算符使任何非零值到 1 并将零保留为零)。 exprpVarDtTm!=NULL 表达式的结果,它只能是 0 (false) 如果在第二个 MFC 源中的调用中的 &m_dt 参数摘录是 本身 NULL – 而且,作为调用它的 class 对象的成员的地址,这似乎不太可能(如果不是 不可能).


您遇到的另一个问题是您似乎误解了 DateTimeStatus GetStatus() const throw(); 声明中的 throw() 规范的含义。作为 described here,它实际上(自 C++17 起)是 noexcept(或 noexcept(true),更准确地说)的别名。要指定一个函数可以抛出 any 类型的异常,应该使用 throw(...)noexcept(false) 规范(或者根本不使用 except/throw 规范).

所以,你的最后一句话不是真的:

I understand this much, if I decide to make a call to GetStatus inside the if clause then we do have a potential for exception to be thrown.

因为 GetStatus() 函数被明确 指定为非抛出,并且您已经 调用了 SetDateTime 成员函数,它(如上所述)可以 抛出异常(但不会,在您的代码中)。