断言是编程中用于检查条件是否满足的机制
通过终止程序等方式来避免 因违反前提条件导致的错误
一、C++中
C++有两种断言:运行时断言和静态断言
1.C++运行时断言
assert(expression)
需包含头文件:assert.h
当expression为false,触发断言,终止程序
它每次执行的时会影响性能,但是它不会出现在发布版本中,因此不影响发布版本的性能
示例:
int Div(int a,int b)
{
assert(b!=0);
return a/b;
}
2.C++静态断言
static_assert(expression,“错误提示”)
在编译时检查,阻止不符合条件的代码通过编译
示例:
template<class T>
T* GetOwningPawn() const
{
//检查模板类型 T是否能转成APawn
static_assert(TPointerIsConvertibleFromTo<T, APawn>::Value, "'T'必须是APawn的子类");
return CastChecked<T>(GetOwner());
}
二、虚幻引擎中
在虚幻引擎中断言有三大族系:check、verify、ensure
1.Check族
当表达式的值为false时 触发断言 导致引擎崩溃
- check(expression)
当表达式的值为false时 触发断言 引擎崩溃
int32 Div(int32 a,int32 b)
{
check(b!=0);
return a/b;
}
- checkf(expression,TEXT(“错误提示”))
触发断言后会向引擎日志中输出错误提示
void DoSomething(const UMyDataAsset* InInputConfig)
{
checkf(InInputConfig,TEXT("Input Config data asset is null!"));
...
}
- checkSlow/checkfSlow
以Slow结尾的check和checkf 表示仅在 Debug 版本生效,适合高频调用的性能敏感场景 - checkCode
可以通过Lambda 表达式检测复杂逻辑
CheckCode([&](){ return 条件表达式 }; )
- checkNoEntry()
无参数,此断言被触发则立即停止,用于被设计为不可到达的代码路径,相当于checkf(false) - checkNoReentry()
无参数 如果执行超过一次,则触发断言 - checkNoRecursion()
无参数 再次被执行时没有离开当前作用域,则触发断言 - unimplemented()
无参数,此断言被触发则立即停止 - 在发布版本启用断言
check断言在发布版本中都不会触发 ,以减少性能开销
使用USE_CHECKS_IN_SHIPPING
宏在发布版本中启用check断言
//启用 移除或设为0,以消除断言开销
#define USE_CHECKS_IN_SHIPPING 1
2.verify族
与check族基本相同
区别在于
在发布版本中check(expression)不触发断言 也不会执行expression表达式
而在发布版本中verify(expression)不触发断言 但会执行expression表达式
verify(b!=0);
verifyf(b!=0,TEXT("The divisor cannot be zero"))
3.ensure族
ensure在触发断言时 不会导致程序终止 引擎不会崩溃
在不执行断言检查的版本中 仍会执行参数表达式
- ensure(expression)
ensure(!CharacterStartUpData.IsNull());
- ensureMsgf(expression,TEXT(“错误提示”))
触发断言后会向引擎日志中输出错误提示
//MyCharacter.h
UPROPERTY(EditDefaultsOnly,BlueprintReadOnly)
TSoftObjectPtr<UMyDataAsset> CharacterStartUpData;
...
//MyCharacter.cpp
ensureMsgf(!CharacterStartUpData.IsNull(),TEXT("forget to assign start up data to %s"),*GetName());
- 由于ensure有一个bool类型的返回值 因此可以用于条件判断语句
if (ensure(!CharacterStartUpData.IsNull()))
{
...
}