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::SetDateTime
、COleDateTime::ConvertSystemTimeToVariantTime
和 AtlConvertSystemTimeToVariantTime
)根据它们的声明都有可能抛出异常(因为他们没有相反的规范,例如 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
并将零保留为零)。 expr
是 pVarDtTm!=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
成员函数,它(如上所述)可以 抛出异常(但不会,在您的代码中)。
查看此代码:
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 thisCOleDateTime
object was set successfully; otherwise,1
. This return value is based on theDateTimeStatus
enumerated type. For more information, see theSetStatus
member function.
对于后一个函数 (SetStatus
),它指出:
The status parameter value is defined by the
DateTimeStatus
enumerated type, which is defined within the COleDateTime class. SeeCOleDateTime::GetStatus
for details.
现在,GetStatus
的文档记录了以下定义:
DateTimeStatus GetStatus() const throw();
因此,如果有错误,它会抛出异常。因此,我决定查看 SetDateTime
:
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::SetDateTime
、COleDateTime::ConvertSystemTimeToVariantTime
和 AtlConvertSystemTimeToVariantTime
)根据它们的声明都有可能抛出异常(因为他们没有相反的规范,例如 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
并将零保留为零)。 expr
是 pVarDtTm!=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 theif
clause then we do have a potential for exception to be thrown.
因为 GetStatus()
函数被明确 指定为非抛出,并且您已经 调用了 SetDateTime
成员函数,它(如上所述)可以 抛出异常(但不会,在您的代码中)。