C++不变式

一、定义

前置条件:指函数履行其契约所必须满足的条件,即此函数可以执行的必须满足的条件。

后置条件:指函数执行完毕后,返回之前哪些条件是调用者可以期望的。

DbC:Design by Contract,契约式设计。

类不变式:一个或一组条件式,对于一个处于良好定义状态的对象总是真的。

 

二、举例说明

比如说,定义了一个时间类CDayTime,如下:

 
  1. class CDayTime

  2. {

  3. public:

  4.  
  5. int AddTime(int iSeconds);

  6.  
  7. private:

  8. void *m_pUser;

  9. int m_iHour;

  10. int m_iMinute;

  11. int m_iSecond;

  12. };

此类很简单,提供一个函数可以增加秒数,然后更新类的成员变量。然后此类保持一个使用者的指针。

 

类的不变式,就是使此类状态有效的条件,我们可以定义一个成员函数来表示:

 
  1. bool CDayTime::IsValid()

  2. {

  3. if (m_iHour < 0 || m_iHour > 23) return false;

  4.  
  5. if (m_iMinute < 0 || m_iMinute > 59) return false;

  6.  
  7. if (m_iSecond < 0 || m_iSecond > 59) return false;

  8.  
  9. return true;

  10. }

AddTime的前置条件可以如下:

assert(m_pUser != NULL);

前置条件并不是对输入参数的检查,而是对内部状态的检查,对某一些必须满足的条件的检查。

 

AddTime的后置条件可以如下:

assert(iRet != 0);

iRet表示函数的返回值,返回值不为0,表示失败。

后置条件是对函数返回值,或返回参数的一个期望,并不能去判断内部状态是否达到了正确的结果,也就是说CDayTime计算的结果是否正确,不能用后置条件来判断。

 

三、基于RAII的自动化检查

RAII(Resource Acquisition Is Initialization,资源获取即初始化),对象的初始化(构造函数调用)包含对它所要管理的资源的获取操作。隐含的意思:对象的析构(析构函数的调用)会自动引发资源的释放(RRID)。

如下,定义自动化检查不变性的类:

 
  1. class CheckInvariant

  2. {

  3. public:

  4. CheckInvariant(CDayTime *pTime)

  5. : m_pTime(pTime)

  6. {

  7. assert(m_pTime->IsValid());

  8. }

  9.  
  10. ~CheckInvariant()

  11. {

  12. assert(m_pTime->IsValid());

  13. }

  14.  
  15. private:

  16. CDayTime *m_pTime;

  17. }

放在AddTime中使用时,如下:

 
  1. int CDayTime::AddTime(int iSeconds)

  2. {

  3. CCheckInvariant oCheck(this);

  4. int iRet;

  5. //计算

  6.  
  7. return iRet;

  8. }

这样就保证了在函数进入和函数返回时都检查不变性。类的不变性函数中一般不放置assert,而是在外部调用处放置。

 

四、实际使用时加上编译选项

为了在实际中方便的去掉和增加这些检查机制,应该增加编译选项,且与_DEBUG或NDEBUG不一样。

 
  1. int CDayTime::AddTime(int iSeconds)

  2. #ifdef ENABLE_DBC

  3. {

  4. assert(m_pUser != NULL);

  5. CCheckInvariant oCheck(this);

  6. int iRet = uncheck_AddTime(iSeconds);

  7. assert(iRet != 0);

  8. return iRet;

  9. }

  10.  
  11. int CDayTime::uncheck_AddTime(int iSeconds)

  12. #endif

  13. {

  14. int iRet;

  15. //计算

  16. return iRet;

  17.  
    原文作者:小明乐
    原文地址: https://blog.csdn.net/catwan/article/details/85229140
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞