C++ 中的大多数语句都已分号结尾,如果编码人员忘记,则会在编译时报错
空语句就是什么也不做的语句,同理也是以分号结尾
//example1.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
; //一个空语句
int i = 0;
while (i < 5)
{
;
i++;
}
while (--i > 0)
; //一个空语句
cout << i << endl; // 0
return 0;
}分号我们漏写会报错,多写了则会将多的当做空语句处理,总之不要多写也不要少些,以免出现逻辑上的 BUG,往往是难以寻找的
//example2.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
int i = 1;
;
;
// int j //error 没有分号
//规范的写法为
// int i = 1;
// int j;
return 0;
}复合语句是指用花括号括起来的语句和声明的序列,复合语句也被称作为块,在函数、for、if、while、switch 等语句都会使用到{}
块不以分号作为结束
//example3.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv) // main本身也是个复合语句块
{
{
int i = 0;
}
// cout << i << endl;//error: 'i' was not declared in this scope
int i = 1;
while (++i < 5)
{
cout << i << endl; // 2 3 4
}
//同理可以有空的块
while (--i > 0)
{
}
return 0;
}可以在块内定义变量,定义在控制结构当中的变量只在响应语句的内部可见,一旦跳出此语句,变量就超出作用范围了
//example4.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
auto i = 1;
{
auto i = 2, j = 666;
cout << i << " " << j << endl; // 2 666
{
auto i = 999;
cout << i << endl; // 999
}
cout << i << endl; // 2
}
cout << i << endl; // 1
return 0;
}if 语句的结构主要有 if、else、else if 组成
//example5.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
bool flag(true);
//简单的if语句
if (flag)
cout << "1" << endl; // 1
if (!flag) // no excute
{
cout << "2" << endl;
}
// if else
if (1 > 2)
{
cout << "1>2" << endl;
}
else
{
cout << "1<=2" << endl; // 1<=2
}
if (1 < 2)
cout << "1<2" << endl; // 1<2
else
cout << "1>=2" << endl;
return 0;
}//example6.cpp
#include <iostream>
#include <cmath>
#include <time.h>
using namespace std;
int main(int argc, char **argv)
{
srand((unsigned)time(NULL));
int i = rand() % 10;
cout << i << endl;
if (i < 4)
{
cout << "i<4" << endl;
}
else if (i >= 4 && i < 7)
{
cout << "i>=4&&i<7" << endl;
}
else if (i >= 7 && i < 9)
{
cout << "i>=7&&i<9" << endl;
}
else
{
cout << "i>=9" << endl;
}
return 0;
}要注意的内容,当 if、else、else if 后面的代码如果没有加{},那么它们默认相当于为后面的一条语句加了花括号,在我们写程序时要格外的注意,以免造成 BUG
在 if 语句嵌套时,它们与 else 是怎样的匹配关系,else 前面如果没有右花括号则它与其上面距离最近的 if 语句匹配,否则花括号强迫 else 与其进行匹配
//example7.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
bool flag = true;
if (flag)
if (!flag)
cout << flag << endl;
else
cout << "else" << endl; // else
//输出 else
// else是与谁进行了匹配呢 else与距离太最近但尚未匹配的if匹配即第2个if
//如果用了括号,就很明显直到它们的匹配关系了,即花括号指明了else与谁对应
if (flag)
{
if (!flag)
{
cout << flag << endl;
}
else
{
cout << "else" << endl; // else
}
}
else
{
cout << "flag is false" << endl;
}
//可能还有很多其他情况
if (flag)
if (!flag)
{
cout << flag << endl;
}
else
cout << "else" << endl; // else
return 0;
}语法格式如下
if (init; condition) {}其中 init 是初始化语句,condition 是条件语句,它们之间使用分号分隔。 允许初始化语句的 if 结构让以下代码成为可能:
#include <iostream>
using namespace std;
// C++17支持初始化语句的if
bool func(int n)
{
return n > 10;
}
int main(int argc, char **argv)
{
int n = 11;
if (bool b = func(n); b)
{
cout << "b=" << n << ">10" << endl;
}
else
{
cout << "b=" << n << "<=10" << endl;
}
return 0;
}
// b=11>10if 初始化语句中声明的变量拥有和整个 if 结构一样长的声明周期,所以前面的代码可以等价于:
#include <iostream>
bool foo()
{
return true;
}
int main()
{
{
bool b = foo();
if (b) {
std::cout << std::boolalpha << "good! foo()=" << b << std::endl;
}
}
}也可以在 if else 中使用
#include <iostream>
using namespace std;
// C++17支持初始化语句的if
bool func(int n)
{
return n > 10;
}
int main(int argc, char **argv)
{
int n = 10;
if (bool b = func(n); b)
{
cout << "b=" << n << ">10" << endl;
}
else if (bool b1 = func(n); !b1)
{
cout << b << " " << b1 << endl;
}
else if (bool b2 = func(n); !b2)
{
cout << b << " " << b1 << " " << b2 << endl;
}
return 0;
}
// 0 0上面代码等价于
#include <iostream>
using namespace std;
bool func(int n)
{
return n > 10;
}
int main(int argc, char **argv)
{
int n = 10;
{
bool b = func(n);
if (b)
{
std::operator<<(std::operator<<(std::cout, "b=").operator<<(n), ">10").operator<<(std::endl);
}
else
{
{
bool b1 = func(n);
if (!b1)
{
std::operator<<(std::cout.operator<<(b), " ").operator<<(b1).operator<<(std::endl);
}
else
{
{
bool b2 = func(n);
if (!b2)
{
std::operator<<(std::operator<<(std::cout.operator<<(b), " ").operator<<(b1), " ").operator<<(b2).operator<<(std::endl);
}
}
}
}
}
}
return 0;
}因为 if 初始化语句声明的变量会贯穿整个 if 结构,所以我们可以利用该特性对整个 if 结构加锁,例如:
#include <mutex>
std::mutex mx;
bool shared_flag = true;
int main()
{
if (std::lock_guard<std::mutex> lock(mx); shared_flag)
{
shared_flag = false;
}
}
// 其他样例
#include <cstdio>
#include <string>
int main()
{
std::string str;
if (char buf[10]{0}; std::fgets(buf, 10, stdin)) {
str += buf;
}
}什么是 switch 语句,它是 if、else if 的升级版 、第一个 case 相当于 if,其余 if else 相当于后面的 case,default 相当于末尾的 else
//example8.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
char ch = 'A';
switch (ch)
{
case 'A':
cout << "A Block" << endl; // A Block
break;
case 'B':
cout << "B Block" << endl;
break;
case 'C':
cout << "C Block" << endl;
break;
default:
cout << "not found" << endl;
break;
}
//等价于
if (ch == 'A')
{
cout << "A Block" << endl; // A Block
}
else if (ch == 'B')
{
cout << "B Block" << endl;
}
else
{
cout << "C Block" << endl;
}
return 0;
}从第一个匹配成功的 case 开始执行向下执行直到遇见 break 或者执行 default 部分代码块后结束
//example9.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
char ch('a');
switch (ch)
{
case 'a':
cout << 'a' << endl;
case 'b':
cout << 'b' << endl;
case 'c':
cout << 'c' << endl;
break;
default:
cout << "not found" << endl;
break;
}
//会出现什么情况呢?当ch为a 或 b 或 c时都会执行从其匹配的地方向后执行
//当 ch=a 输出 a b c
//当 ch=b 输出 b c
//当 ch=c 输出 c
switch (ch)
{
case 'a':
cout << 'a' << endl;
case 'b':
cout << 'b' << endl;
case 'c':
cout << 'c' << endl;
default:
cout << "not found" << endl;
break;
}
// ch=a时 输出a b c not found
return 0;
}要值得注意的是,case 标签必须为整形常量表达式,任何两个 case 标签的值不能相同,否则会引发错误。 不在每个 case 代码内些 break,本就违反了我们用 switch 的初心,而且没写好就会因漏写 chanshnegBUG。
可以在 case 要执行的代码中定义变量,但其中并没有想象的那么简单
//example10.cpp
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char **argv)
{
int v = 0;
switch (v)
{
case 0:
// i = 999; 错误i还没有声明
// cout << i << endl;
case 1:
// std::string filename;//错误 控制流可能绕过此初始化语句 string有隐式初始化
// cout << filename << endl;
// int j = 0;错误 控制流可能绕过此初始化语句
int i;
break;
case 2:
i = 9;
cout << i << endl; // 9
break;
default:
break;
}
//其实相当于在case内声明变量,相当于在此case执行时声明变量,在后面各个case内都能使用
//为什么不能初始化
//总之可能被绕开的初始化语句是不正确的写法
return 0;
}如何解决这种问题呢
在花括号内初始化、仅在花括号内使用
//example11.cpp
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char **argv)
{
int v = 0;
switch (v)
{
case 0:
// i = 999; 错误i还没有声明
// cout << i << endl;
case 1:
{
std::string filename = "cdc";
cout << filename << endl; // cdc
int j = 0;
}
// cout << j << endl; undefined因为filename与j的作用域仅仅在上面的花括号内
int i;
// break;
case 2:
i = 9;
cout << i << endl; // 9
break;
default:
break;
}
//其实相当于在case内声明变量,相当于在此case执行时声明变量,在后面各个case内都能使用
//为什么不能初始化
//总之可能被绕开的初始化语句是不正确的写法
return 0;
}
//输出 cdc 9下面代码中 continue 的作用域其实是为 for 循环内的代码块范围
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
int n = 10;
for (int i = 0; i < 10; i++)
{
cout << i << endl;
switch (n)
{
case 0:
{
break;
}
default:
{
continue;
}
}
cout << i << endl;
}
return 0;
}
// 0 1 2 3 4 5 6 7 8 9如果没有在 while、for、do while 循环内,单纯在 switch 内使用 continue 是编译不过的。
和 C++17 支持初始化语句的 if 类似
#include <iostream>
using namespace std;
// C++17支持初始化语句的switch
bool func(int n)
{
return n > 10;
}
int main(int argc, char **argv)
{
int n = 10;
switch (bool b = func(n); b)
{
case false:
cout << "func(" << n << ")="
<< "false" << endl;
break;
default:
cout << "func(" << n << ")="
<< "true" << endl;
break;
}
return 0;
}
// func(10)=false例如 switch 配和条件变量使用
#include <iostream>
#include <condition_variable>
#include <chrono>
#include <mutex>
using namespace std::chrono_literals;
std::condition_variable cv;
std::mutex cv_m;
int main()
{
switch (std::unique_lock<std::mutex> lk(cv_m); cv.wait_for(lk, 1000ms))
{
case std::cv_status::timeout:
{
std::cout << "timeout" << std::endl;
}
break;
case std::cv_status::no_timeout:
{
std::cout << "no_timeout" << std::endl;
}
break;
}
}
// timeout迭代语句在 C++内主要有四大类,while 语句、传统的 for 循环、范围的 for 循环、do while 语句
语法格式
while(condition)
lang;
while(condition){
code block
}代码样例
//example12.cpp
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main(int argc, char **argv)
{
vector<int> vecs{1, 2, 3, 4};
string str = "1234";
int i = 0;
auto vec_iter = vecs.begin();
while (vec_iter != vecs.end() && i < str.size())
{
cout << *vec_iter << endl;
cout << str.at(i) << endl;
i++;
vec_iter++;
}
// 1 1 2 2 3 3 4 4
return 0;
}语法格式
for(init-statemen;condition;expression)
lang;
for(init-statemen;condition;expression){
}代码样例
//example13.cpp
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char **argv)
{
for (int i = 0; i < 5; i++)
{
cout << i << endl;
} // 0 1 2 3 4
int count = 0;
for (int i = 0, j = 0; i < 5; i++, j++)
{
cout << i << " " << j << endl;
count++;
} // 0 0 1 1 2 2 3 3 4 4
cout << count << endl; // 5
vector<int> vecs = {1, 2, 3, 4};
vector<int>::iterator iter;
for (iter = vecs.begin(); iter != vecs.end(); iter++)
{
cout << *iter << endl;
} // 1 2 3 4
// init-statemen与expression可以为空,但condition不能为空
return 0;
}基于范围的 for 循环语法 C++11 标准引入了基于范围的 for 循环特性,该特性隐藏了迭代器的初始化和更新过程,让程序员只需要关心遍历对象本身,其语法也比传统 for 循环简洁很多:
基于范围的 for 循环不需要初始化语句、条件表达式以及更新表达式,取而代之的是一个范围声明和一个范围表达式。其中范围声明是一个变量的声明,其类型是范围表达式中元素的类型或者元素类型的引用。而范围表达式可以是数组或对象,对象必须满足以下 2 个条件中的任意一个。
begin 和 end 函数不必返回相同类型,在 C++11 标准中基于范围的 for 循环相当于以下伪代码:
{
auto && __range = range_expression;
for (auto __begin = begin_expr, __end = end_expr; __begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}其中begin_expr和end_expr可能是__range.begin()和__range.end(),或者是begin(__range)和end(__range)。
当然,如果__range是一个数组指针,那么还可能是__range和__range+__count(其中**count
是数组元素个数)。
这段伪代码有一个特点,它要求begin_expr和end_expr返回的必须是同类型的对象。
但实际上这种约束完全没有必要,只要begin != __end能返回一个有效的布尔值即可,
所以 C++17 标准对基于范围的 for 循环的实现进行了改进,伪代码如下:
{
auto && __range = range_expression;
auto __begin = begin_expr;
auto __end = end_expr;
for (; __begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}代码样例
//example14.cpp
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main(int argc, char **argv)
{
vector<int> vecs{1, 2, 3, 4};
for (auto &num : vecs)
{
cout << num << endl;
num++;
} // 1 2 3 4
for (int &num : vecs)
{
cout << num << endl;
num--;
} // 2 3 4 5
string str = "abcd";
for (char &ch : str)
{
cout << ch << endl;
} // abcd
return 0;
}auto&&无论是 C++11 还是 C++17 标准,基于范围的 for 循环伪代码都是由以下这句代码开始的:
auto && __range = range_expression;理解了右值引用的读者应该敏锐地发现了这里存在的陷阱
auto &&。对于这个赋值表达式来说,
如果range_expression是一个纯右值,
那么右值引用会扩展其生命周期,保证其整个 for 循环过程中访问的安全性。
但如果range_ expression是一个泛左值,
那结果可就不确定了,参考以下代码:
class T {
std::vector<int> data_;
public:
std::vector<int>& items() { return data_; }
// …
};
T foo()
{
T t;
return t;
}
for (auto& x : foo().items()) {} // 未定义行为这里的 for
循环会引发一个未定义的行为,因为foo().items()返回的是一个泛左值类型std::vector&,于是右值引用无法扩展其生命周期,导致
for 循环访问无效对象并造成未定义行为。
对于这种情况请读者务必小心谨慎,将数据复制出来是一种解决方法:
T thing = foo();
for (auto & x :thing.items()) {}在 C++20 标准中,基于范围的 for 循环增加了对初始化语句的支持, 所以在 C++20 的环境下我们可以将上面的代码简化为:
for (T thing = foo(); auto & x :thing.items()) {}此部分为高级进阶内容
operator *、operator !=和operator ++运算符函数。#include <iostream>
using namespace std;
// int类型指针迭代器
class IntIter
{
public:
IntIter(int *p) : p_(p) {}
bool operator!=(const IntIter &other)
{
return (p_ != other.p_);
}
const IntIter &operator++()
{
p_++;
return *this;
}
int operator*() const
{
return *p_;
}
private:
int *p_;
};
// 模板容器
template <unsigned int fix_size>
class FixIntVector
{
public:
FixIntVector(std::initializer_list<int> init_list)
{
if (init_list.size() != fix_size)
{
return;
}
int *cur = data_;
for (auto e : init_list)
{
*cur = e;
cur++;
}
}
IntIter begin()
{
return IntIter(data_);
}
IntIter end()
{
return IntIter(data_ + fix_size);
}
private:
int data_[fix_size]{0};
};
int main(int argc, char **argv)
{
FixIntVector<10> fix_int_vector{1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
for (int e : fix_int_vector)
{
cout << e << " ";
}
// 1 2 3 4 5 6 7 8 9 0
return 0;
}do while 语句与 while 语句非常相似,唯一的区别是,do while 语句先执行循环体后检查条件,不管条件值如何,我们都至少执行一次循环。
语法格式
do
statement
while(condition);代码样例
//example15.cpp
#include <iostream>
using namespace std;
int get_num()
{
static int num = 0;
++num;
return num;
}
int main(int argc, char **argv)
{
int num;
do
{
num = get_num();
cout << num << endl;
} while (num < 5);
// 1 2 3 4 5
do
cout << get_num() << endl;
while (false);
// 6
return 0;
}主要有三种操作 break 语句、continue 语句、goto 语句
负责终止离它最近的 while、do while、for、switch 语句,并从这些语句之后的第一条语句开始继续执行
//example16.cpp
#include <iostream>
#include <string>
using namespace std;
int main(int argc, char **argv)
{
int i = 0;
do
{
cout << i << endl;
if (i > 3)
{
break; //离开do while循环
}
i++;
} while (i < 10); // 0 1 2 3 4
string str = "abcd";
for (char &ch : str)
{
if (ch == 'c')
break; //离开for循环
switch (ch)
{
case 'a':
cout << ch << endl;
break; //离开switch语句
case 'b':
cout << ch << endl;
break; //离开switch语句
default:
break; //离开switch语句
}
int num = 1;
while (num <= 3)
{
if (num == 3)
break; //离开while循环
cout << num << endl;
num++;
}
} // a 1 2 b 1 2
return 0;
}continue 语句终止最近的循环中的当前迭代并立即开始下一次迭代,continue 只能出现在 for、while、do while 循环内,或嵌套在此类循环里的语句或块的内部。
//example17.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
int num = 1;
do
{
num++;
if (num == 2)
{
continue;
}
cout << num << endl;
} while (num < 4); // 3 4
/*
//下面将会卡死 因为当num为2时将会continue重新执行do内的代码,导致num一直只能为2
num = 1;
do
{
if (num == 2)
{
continue;
}
num++;
cout << num << endl;
} while (num < 4);
*/
num = 1;
while (num < 3)
{
num++;
if (num == 2)
{
continue;
}
cout << num << endl;
// num++;//将num++移到此处也将会num停留在2卡死不断地continue
} // 3
// for循环不同的一点时,当continue后,先回执行i++然后执行判断i<=4然后再次进行循环
// 而在do while与while中我们要注意使用continue可能导致死循环
for (int i = 1; i <= 4; i++)
{
if (i == 3)
{
continue;
}
cout << i << endl; // 1 2 4
}
return 0;
}goto 语句的作用是从 goto 语句无条件跳转到同意函数的内的另一条语句
//example18.cpp
#include <iostream>
using namespace std;
void func()
{
goto L1;
cout << "C++" << endl;
L1:
cout << "hello world" << endl;
}
int main(int argc, char **argv)
{
int i = 0;
goto L1;
cout << i << endl; //不执行
// int j = 999; //错误:goto语句不能跳过带初始化的变量定义
int j; //但可以跳过定义但未被初始化的语句
L1:
cout << ++i << endl; // 1
// int j = 999; //错误:j已经被定义过了
cout << j << endl; // undefined
func(); // hello world
//标签的作用域在函数内部
return 0;
}C++有运行时异常处理机制
抛出异常
//example19.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
int i = -1;
if (i != 1)
{
throw runtime_error("i!=1");
}
/*
terminate called after throwing an instance of 'std::runtime_error'
what(): i!=1
*/
cout << i << endl; //不会被执行
return 0;
}try 语句块至少需要一个 catch 语句块
//example20.cpp
#include <iostream>
using namespace std;
int main(int argc, char **argv)
{
int i = -1;
// try代码块内可能抛出多种类型的异常,如果要进行对有可能出现的异常处理,则使用多个catch
try
{
if (i > 1)
{
throw runtime_error("i>1");
}
if (i < 0)
{
throw logic_error("i<0");
}
cout << "try end" << endl; //如果上面有throw抛出异常,则在throw执行后,try内代码停止运行
}
catch (runtime_error error)
{
cout << error.what() << endl;
throw runtime_error(error.what());
}
catch (logic_error error)
{
cout << error.what() << endl; // i<0
}
cout << i << endl; //-1
return 0;
}//example21.cpp
#include <iostream>
using namespace std;
void func()
{
//抛出runtime_error没有对应的catch,则会停止此函数的执行
//然后将error对象传递到调用它的函数
try
{
throw runtime_error("func test error");
cout << "func try end" << endl;
}
catch (logic_error error)
{
cout << error.what() << endl;
}
}
int main(int argc, char **argv)
{
// func();
//如果调用则会抛出异常,不处理则相当于再次throw了一个error
//仍旧没有捕获,则main函数停止运行
//正确方式
try
{
func();
}
catch (runtime_error error)
{
cout << error.what() << endl;
}
cout << "main end" << endl;
return 0;
}
//程序输出
// func test error
// main endC++标准库定义类一组类,用于报告标准库函数遇到的问题
#include<exception>
定义了最通用的异常类exception,它只报告异常的发生,不提供任何额外信息
exception类是个接口,并不能实例化#include<stdexcept>
exception 最常见的问题
runtime_error 继承exception:只有在运行时才能检测出的问题
range_error 运行时错误,生成的结果超出了有意义的值域范围
overflow_error 继承运行时错误:计算上溢
underflow_error 继承运行时错误:计算下溢
logic_error 继承exception:程序逻辑错误
domain_error 继承逻辑错误:参数对应的结果值不存在
invalid_argument 继承逻辑错误:无效参数
length_error 继承逻辑错误:视图创建一个超出该类型最大长度的对象
out_of_range 继承逻辑错误:使用一个超出有效范围的值#include<new>//中定义bad_alloc异常类
#include<type_info>//中定义bad_cast异常类#include <iostream>
#include <stdexcept>
#include <cmath>
#include <ctime>
#include <new>
using namespace std;
int main(int argc, char **argv)
{
time_t t;
srand((unsigned)time(&t));
int num = rand() % 13;
num = 7;
try
{
try
{
switch (num)
{
case 0:
throw runtime_error("runtime_error");
break;
case 1:
throw logic_error("logic_error");
case 2:
throw bad_alloc();
case 3:
throw bad_exception();
case 4:
throw bad_typeid();
case 5:
throw domain_error("domain_error");
case 6:
throw invalid_argument("invalid_argument");
case 7:
throw length_error("length_error");
case 8:
throw out_of_range("out_of_range");
case 9:
throw overflow_error("overflow_error");
case 10:
throw range_error("range_error");
case 11:
throw underflow_error("underflow_error");
default:
throw bad_cast();
break;
}
}
catch (length_error &error)
{
cout << "length_error:: " << error.what() << endl;
throw error;
}
catch (exception &error) //所有类型的异常都会被捕获,因为所有异常类继承exception类
{
cout << error.what() << endl;
}
}
catch (exception &error)
{
cout << "try block throw::" << error.what() << endl;
}
cout << "main end" << endl;
return 0;
//当num=7时输出
//length_error:: length_error
//try block throw::length_error
//main end
}