一、定义
前置条件:指函数履行其契约所必须满足的条件,即此函数可以执行的必须满足的条件。
后置条件:指函数执行完毕后,返回之前哪些条件是调用者可以期望的。
DbC:Design by Contract,契约式设计。
类不变式:一个或一组条件式,对于一个处于良好定义状态的对象总是真的。
二、举例说明
比如说,定义了一个时间类CDayTime,如下:
-
class CDayTime -
{ -
public: -
int AddTime(int iSeconds); -
private: -
void *m_pUser; -
int m_iHour; -
int m_iMinute; -
int m_iSecond; -
};
此类很简单,提供一个函数可以增加秒数,然后更新类的成员变量。然后此类保持一个使用者的指针。
类的不变式,就是使此类状态有效的条件,我们可以定义一个成员函数来表示:
-
bool CDayTime::IsValid() -
{ -
if (m_iHour < 0 || m_iHour > 23) return false; -
if (m_iMinute < 0 || m_iMinute > 59) return false; -
if (m_iSecond < 0 || m_iSecond > 59) return false; -
return true; -
}
AddTime的前置条件可以如下:
assert(m_pUser != NULL);
前置条件并不是对输入参数的检查,而是对内部状态的检查,对某一些必须满足的条件的检查。
AddTime的后置条件可以如下:
assert(iRet != 0);
iRet表示函数的返回值,返回值不为0,表示失败。
后置条件是对函数返回值,或返回参数的一个期望,并不能去判断内部状态是否达到了正确的结果,也就是说CDayTime计算的结果是否正确,不能用后置条件来判断。
三、基于RAII的自动化检查
RAII(Resource Acquisition Is Initialization,资源获取即初始化),对象的初始化(构造函数调用)包含对它所要管理的资源的获取操作。隐含的意思:对象的析构(析构函数的调用)会自动引发资源的释放(RRID)。
如下,定义自动化检查不变性的类:
-
class CheckInvariant -
{ -
public: -
CheckInvariant(CDayTime *pTime) -
: m_pTime(pTime) -
{ -
assert(m_pTime->IsValid()); -
} -
~CheckInvariant() -
{ -
assert(m_pTime->IsValid()); -
} -
private: -
CDayTime *m_pTime; -
}
放在AddTime中使用时,如下:
-
int CDayTime::AddTime(int iSeconds) -
{ -
CCheckInvariant oCheck(this); -
int iRet; -
//计算 -
return iRet; -
}
这样就保证了在函数进入和函数返回时都检查不变性。类的不变性函数中一般不放置assert,而是在外部调用处放置。
四、实际使用时加上编译选项
为了在实际中方便的去掉和增加这些检查机制,应该增加编译选项,且与_DEBUG或NDEBUG不一样。
-
int CDayTime::AddTime(int iSeconds) -
#ifdef ENABLE_DBC -
{ -
assert(m_pUser != NULL); -
CCheckInvariant oCheck(this); -
int iRet = uncheck_AddTime(iSeconds); -
assert(iRet != 0); -
return iRet; -
} -
int CDayTime::uncheck_AddTime(int iSeconds) -
#endif -
{ -
int iRet; -
//计算 -
return iRet;
版权声明
本文仅代表作者观点,不代表百度立场。
本文系作者授权百度百家发表,未经许可,不得转载。


