一、 断言,主要用于在函数入口处进行参数检查,是否符合参数设置要求;
(1)true:继续执行;false:终止运行;
(2)特点:在程序运行时才能起作用,进行判断;
(3)头文件:<cassert>
(4)断言的代码一般放在函数或者成员函数的第一行,表达式多为函数的形参;
(5)静态断言static_assert可以自定义违反断言时的错误提示信息,提示信息一般是一段字符串;一般很少用,因为其要求判断的表达式必须是常量,一般用配合constexpr修饰的变量(在编译器优化为常量);
#include <iostream>
#include <cassert>
using namespace std;
void func(int * ptr1, int * ptr2)
{
assert(ptr1 && ptr2);
cout<<(*ptr1)<<" : "<<(*ptr2)<<endl;
}
int main(int argc, char **argv)
{
int a = 5;
int b = 6;
func(&a, &b);
func(&a, nullptr);
return 0;
}
二、 异常
(1)异常与断言不同,异常不会立即终止程序运行;是一种面向对象语言处理错误的一种方式;
(2)当一个函数出现自己无法处理的错误时,可以抛出异常,然后利用异常捕捉去处理;
(3)语法,三个关键字;
①try:用于包含可能会出现异常的代码块或者函数,后面通常跟一个或者多个catch块;
②catch:用于捕捉异常;形式多样;其中catch(...)可以捕捉任意类型的异常;可以多个catch排序,异常信息会从上到下找到适配参数的catch块执行;
③throw:当问题出现时,throw可以抛出一个异常;
#include <iostream>
#include <cassert>
#include <string>
using namespace std;
void func(char * str1, char *str2)
{
assert(str1 != nullptr);
if(str2 == nullptr)
{
throw ("str2 is nullptr");
}
cout<<"ok"<<endl;
}
void pfunc(char * str1, char *str2)
{
assert(str1 != nullptr);
if(str2 == nullptr)
{
throw (777);
}
cout<<"ok"<<endl;
}
int main(int argc, char **argv)
{
char name[10];
try
{
func(name,nullptr);
pfunc(name,nullptr);
}
catch(const char * errmsg)
{
cout<<errmsg<<endl;
}
try //这里加一个try主要是因为当上面try内的两个函数均出现异常时,第二个异常无法显示;
{
pfunc(name,nullptr);
}
catch(int error)
{
cout<<"error num : "<<error<<endl;
}
return 0;
}
(4)一些函数是不允许出现异常的,比如构造函数(若有异常,可能会导致对象不完整或者没有完全初始化)、析构函数(若有异常可能导致内存泄漏);
(5)关键字noexcept
①主要用在一个操作或者函数不可能抛出任何异常,放在这个函数的后面;这样会给编译器更大的优化空间;
②使用noexcept的时机:构造函数、析构函数;
(6)自定义异常体系,通过建立一个异常基类,派生类继承这个类,从而来定义出不同的异常;
#include <iostream>
#include <string>
using namespace std;
//基类 异常
class Exception
{
public:
Exception(const char* str = nullptr, int id = 0) : _errmsg(str), _id(id)
{
}
virtual void what()const = 0; //纯虚函数,多态机制
protected:
string _errmsg;//错误信息
int _id;//错误码
};
//派生类
//数据库异常
class SqlException : public Exception
{
public:
SqlException(const char *str = nullptr, int id = 1) : Exception(str, id)
{
}
virtual void what()const
{
cout << "error msg:" << _errmsg << endl;
cout << "error id:" << _id << endl;
}
};
//网络异常
class HttpException :public Exception
{
public:
HttpException(const char *str = nullptr, int id = 2) : Exception(str, id)
{
}
virtual void what()const{
cout << "error msg:" << _errmsg << endl;
cout << "error id:" << _id << endl;
}
};
//缓存异常
class CacheException :public Exception
{
public:
CacheException(const char *str = nullptr, int id = 3) : Exception(str, id)
{
}
virtual void what()const
{
cout << "error msg:" << _errmsg << endl;
cout<< "error id:" << _id << endl;
}
};
void test()
{
//当网络连接失败,抛出这个异常即可
//throw HttpException("Http fail", 2);
//当缓存错误
//throw CacheException("Cache error", 3);
//当数据库错误
throw SqlException("Sql error", 4); //在抛出了一个派生类对象前,先初始化了一个类;
}
int main()
{
try
{
test();
}
//用基类捕捉即可
catch (const Exception& a) //捕捉基类即可,会利用多态调用派生类的成员函数;
{
a.what(); //捕捉到异常后,调用该派生类对象的成员函数,将错误信息和错误代码打印;
}
catch (...)
{
cout << "unknow exception" << endl;
}
system("pause");
return 0;
}
(7)异常的优缺点
优点:①更好的返回和描述错误信息,便于定位;②对于某些错误,不需要终止程序;
缺点:①降低了代码可续性;②c++标准库的自定义体系不够细致,太笼统,需要我们自己定义各自的体系,造成混乱;