异常处理
在程序执行时出现错误,例如文件路径有误造成无法读取,异常处理提供了一种转移程序控制权的方法。
C++ 的异常处理流程:在 try 中的代码抛出异常,由 catch 进行捕获异常。异常可以是系统自带的类型,也可以利用 throw 主动抛出异常,可以对异常类 exception 进行继承和重载来定义新异常。
代码中每次执行可能会抛出不同类型的异常,可以通过多个 catch 来捕获分别做出处理。
try
{}catch( ExceptionName e1 )
{}catch( ExceptionName e2 )
{}catch( ExceptionName e3 )
{}
throw 抛出异常
使用 throw 在任何地方都可抛出异常。throw 语句的操作数可以是任意的表达式,表达式的结果的类型决定了抛出的异常类型。
if( b == 0 )
{
throw "Division by zero condition!";
}
return (a/b);
捕获异常
catch 块跟在 try 块后面,用于捕获异常,还可以指定想要捕捉的异常类型
try
{}catch( ExceptionName e )
{
// 处理 ExceptionName 异常的代码
}
标准异常
在 C++ 的 <exception>
中已经定义了在程序中标准的异常。
定义新的异常
可以通过继承和重载 exception 类来定义新异常。
struct MyException : public exception
{
const char * what () const throw ()
{
return "C++ Exception";
}
};
throw MyException();
可以发现这里用的是结构体继承,在 C++ 中结构体的功能有极大的扩展,拥有了类的功能,唯一的区别在默认的继承访问权限,struct是public,class是private。但是我们依旧将类变量为成员,结构体变量为数据。并且类与结构体之间是可以相互继承的。
const char *
:是返回类型,一个指向常量字符的指针what()
:异常类提供的一个公共方法,它已被所有子异常类重载const
:声明为const函数,throw ()
:声明该函数不抛出异常
动态内存
C++ 程序中的内存分为以下五个部分:
- 栈区:由编译器在需要时自动分配,不需要时自动清除的变量存储区。通常存放局部变量、函数参数等。
- 堆区:是由 new 分配的内存块,new 与 delete 对应。如果没有手动释放掉,资源将由操作系统在程序结束后自动回收。
- 自由存储区:是由 malloc 等分配的内存块,用free来释放,和堆十分相似,是同一块区域(其实 new 底层调用的是 malloc)。
- 全局/静态存储区:全局变量和静态变量被分配到同一块内存中。
- 常量存储区:这是一块特殊存储区,里边存放常量,不允许修改。
new 和 delete 运算符
- new:为给定类型的变量在运行时分配堆内的内存,这会返回所分配的空间地址。‘
- delete:删除之前由 new 运算符分配的内存。
double* pvalue = NULL; // 初始化为 null 的指针
if( !(pvalue = new double )) // 为变量请求内存,并检查是否成功分配
{
cout << "Error: out of memory." <<endl;
exit(1);
}
*pvalue = 29494.99; // 在分配的地址存储值
delete pvalue; // 释放 pvalue 所指向的内存
模板
模板是泛型编程的基础,泛型编程即以一种独立于任何特定数据类型的方式编写代码。即在函数或类定义时并没有固定数据类型,而是在使用时指定。
函数模板
一般形式如:
template <typename type>
ret-type func-name(parameter list)
{
}
type 是函数所使用的数据类型的占位符名称。例子如下:
template <typename T> // 占位符为 T,在被调用时才能确定
inline T const& Max (T const& a, T const& b) // 创建一个内联函数 **Max**,返回类型为 T,形参为常值引用调用
{
return a < b ? b:a;
}
Max(int 1,int 2)
类模板
对类的定义也是类似的手法,可以定义相同的操作,拥有不同数据类型的成员属性。
template <typename T> // 申明为类模型,以下遇到 T 后表示为一种泛型
class Complex{
public:
//构造函数
Complex(T a, T b)
{
this->a = a;
this->b = b;
}
//运算符重载
Complex<T> operator+(Complex &c)
{
Complex<T> tmp(this->a+c.a, this->b+c.b);
return tmp;
}
private:
T a;
T b;
}
int main()
{
//对象的定义,必须声明模板类型,因为要分配内容
Complex<int> a(10,20);
Complex<int> b(20,30);
Complex<int> c = a + b;
return 0;
}
预处理器
预处理器是指示编译器在实际编译之前所需完成的预处理内容。所有的预处理器指令都是以井号(#)开头,只有空格字符可以出现在预处理指令之前。预处理指令不是 C++ 语句,所以它们不会以分号(;)结尾。
#define 定义一个预处理宏
#undef 取消宏的定义
#if 编译预处理中的条件命令,相当于C语法中的if语句
#ifdef 判断某个宏是否被定义,若已定义,执行随后的语句
#ifndef 与#ifdef相反,判断某个宏是否未被定义
#elif 若#if, #ifdef, #ifndef或前面的#elif条件不满足,则执行#elif之后的语句,相当于C语法中的else-if
#else 与#if, #ifdef, #ifndef对应, 若这些条件不满足,则执行#else之后的语句,相当于C语法中的else
#endif #if, #ifdef, #ifndef这些条件命令的结束标志.
#define 预处理
#define 预处理指令用于创建符号常量。该符号常量通常称为宏
#define PI 3.14159
int main ()
{
cout << "Value of PI :" << PI << endl;
}
#define 还可以来定义一个带有参数的宏
#define MIN(a,b) (a<b ? a : b)
int main ()
{
int i, j;
i = 100;
j = 30;
cout <<"较小的值为:" << MIN(i, j) << endl;
}
条件编译
有选择地对部分程序源代码进行编译。
#ifdef NULL
#define NULL 0
#endif
# 和 ## 运算符
#
字符串化的意思,出现在宏定义中的#是把跟在后面的参数转换成一个字符串
#define MKSTR( x ) #x
int main ()
{
cout << MKSTR(HELLO C++) << endl; // 转换成了:cout << "HELLO C++" << endl;
}
##
连接符号,把参数连在一起
#define concat(a, b) a ## b
int main()
{
int xy = 100;
cout << concat(x, y); // 转换成了:cout << xy;
}
评论区