C++基础知识面试必备、复习细节 (8)(tuple、随机数、异常处理、多继承)
tuple类型
-
tuple是一个类似于pair的模板,pair类型是每个成员变量各自可以是任意类型,但是只能有两个成员,而tuple与pair不同的是它可以有任意数量的成员
-
个人常把tuple当作一个结构体使用,如果只是一次使用的话就避免了结构体繁琐的创建
-
tuple的基本操作:
tuple<int,string,int> t{1,"James",1000}; //可以用来存一个序号,名字,工资这样的信息 auto t=make_tuple("1","James","12345678911"); //存序号,名字,电话 用auto可以自动判断 auto name=get<1>(t); //返回名字 通过get<i>(tuple)返回第i-1个成员对象 get<2>(t)="98765432111"; //get<>()返回的是引用,因此可以修改
-
两个tuple相等当且仅当元素数量相同且每个元素相等,< 也是同理
-
通过使用tuple可以令一个函数中返回多个值, 有时候可以令程序更简洁方便
随机数
-
头文件: random
-
c++的随机数通过两部分解决随机数问题:随机数引擎类和随机数分布类
随机数引擎类生成随机的unsigned整数序列,随机数分布类使用引擎返回特定概率分布的随机数
-
常用的默认随机数引擎生成随机数:
default_random_engine e; for (int i = 0; i < 10;i++) cout << e() << ' '; /* 16807 282475249 1622650073 984943658 1144108930 470211272 101027544 1457850878 1458777923 2007237709 */
-
使用分布类型的对象得到指定范围的随机数
uniform_int_distribution<unsigned> u(0, 9); //生成0-9的随机数 default_random_engine e; for (int i = 0; i < 10; i++) cout << u(e) << ' '; //0 1 7 4 5 2 0 6 6 9
-
一个给定的随机数发生器一直会生成相同的随机数序列。一个函数如果定义了局部的随机数发生器,应该将其(包括引擎和分布对象)定义为static的,否则每次调用生成相同的序列。
-
生成正态分布序列
default_random_engine e; normal_distribution<> n(4, 1.5); //均值为4,标准差为1.5的正态分布 for (int i = 0; i < 10; i++) cout << n(e) << ' '; //3.81705 2.36977 5.02643 2.38722 4.0499 5.11725 4.05041 3.21004 4.6938 4.30105
-
生成伯努利分布序列(true or false 可以制定概率)
default_random_engine e; bernoulli_distribution n(0.9); //伯努利实验的p=0.9 for (int i = 0; i < 10; i++) cout << n(e) << ' '; //1 1 1 1 0 1 1 1 1 1
IO库
-
八进制十六进制十进制
cout<<hex<<20<<endl; //14 将cout输出的整数转换为16进制 cout<<oct<<10<<endl; //12 将cout输出的整数转换为8进制 cout << dec << 10<<endl;//10 将cout输出的整数转换为10进制 cout<<0x20<<endl; //0x开头表示十六进制 32 cout << 010 << endl; //0开头表示八进制 8
-
浮点数输出格式
//制定打印精度 double t = 3.1415926; cout.precision(3); //浮点数精度为3 cout << t << endl; //3.14 cout.precision(5); cout <<t << endl; //3.1416
异常处理
-
执行throw时,跟在throw后面的语句将不再执行,程序的控制权从throw转移到catch块。因此,尽量不要在析构函数中写异常处理,避免内存泄漏。
-
栈展开:对于抛出异常的函数的调用语句位于一个try语句块内,则检查与该try块关联的catch语句。如果找到了匹配的catch,则用该catch处理异常,否则如果该try语句嵌套在其他try块中,则继续检查与外层try匹配的catch语句,如果还没找到则退出当前的主调函数,继续在调用了这个函数的其他函数中寻找。
栈展开过程也就是沿着嵌套函数的调用链不断查找,直到找到了与异常匹配的catch子句为止,或者一直没找到,退出主函数后终止查找过程。
-
一个异常没被捕获,则将终止程序
-
构造函数中抛出异常,会导致析构函数不能被调用,但对象本身已申请到的内存资源会被系统释放
因为析构函数不能被调用,所以可能会造成内存泄露或系统资源未被释放
构造函数中可以抛出异常,但必须保证在构造函数抛出异常之前,把系统资源释放掉,防止内存泄露
-
不要在析构函数中抛出异常
如果某个操作可能会抛出异常,class应提供一个普通函数(而非析构函数),来执行该操作
如果析构函数中异常非抛不可,那就用try catch来将异常吞下,不要让异常逃离析构函数
-
try { //something } catch (exception &e) { cout << "Standard exception: " << e.what() << endl; }
多继承
- 如果从多个基类中继承了相同的构造函数,则该类必须定义自己的构造函数
- 当一个类拥有多个基类时,可能出现派生类从两个或多个基类中继承了同名成员的情况,则需要用前缀限定符访问该函数,否则会有二义性
- 虚继承:令某个类做出声明,使其共享基类。在这样的情况下,虚基类被多次继承,也只有一个共享的基类 virtual public A
- 虚派生只影响从指定了虚基类的派生类中进一步派生出的类,它不会影响派生类本身
- 虚基类总是先于非虚基类构造,与它们在继承体系中的次序和为止无关
- 多继承的父类的构造顺序,只与继承的顺序有关,与成员初始化列表顺序无关
- 多继承的构造函数的顺序:
- 虚基类的构造函数按照它们被继承的顺序构造
- 非虚基类的构造函数按照它们被继承的顺序构造;
- 成员对象的构造函数按照它们声明的顺序调用
- 类自己的构造函数
- 析构函数调用顺序与构造函数相反