锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

c++高级编程学习笔记6

时间:2022-10-20 13:30:00 各种组合二极管badwl的二极管二极管后面带tp

其他库工具

ratio 库

可通过 ratio 在编译过程中,库准确地表示任何有限的有限理数。ratio 对象在 std::chrono::duration 类中使用。所有与理数相关的内容都在头文件中定义,并且都在 std 在名称空间中。通过类型对有理数的分子和分母进行分类 std::intmax_t 编译时常量表示,这是一种具有符号的整数类型,其最大宽度由编译器指定。由于这些有理数编译的特点,在使用时看起来复杂而不寻常。ratio 对象的定义不同于普通对象, 而且不能调用 ratio 对象的方法。类型别名需要使用。例如,下面的代码定义了一个表示 1/60 有理数编译常量:

using r1 = ratio<1,60>; 

rl 有理数的分子和分母是编译常数,可以通过以下方式访问:

intmax_t num = r1::num;intmax_t den = r1::den; 

记住 ratio 也就是说,编译时需要确定分子和分母。以下代码会导致编译错误;

intmax_t n = 1;intmax_t d = 60;using r1 = ratio<n,d>; //Error 

将n和d 定义为常量就不会有编译错误了:

const intmax_t n = 1; const intmax_t d = 60; using r1 = ratio<n,d>; //Ok 

有理数总是简化的。 ratio,计算最大公约数 gcd、分子 num 和分母 den 定义如下:

num = sign(n)*sign(d)*abs(n)/gcb; den = abs(d)/gcb; 

ratio 库支持有理数的加法、减法、乘法和除法操作。由于所有这些操作都是在编译过程中进行的,因此不应使用标准算术操作符,而应使用特定的模板和类型别名组合。可用的算术 ratio 模板包括 ratio_ add、ratio_subtract ratio_multiply 和 ratio_divide。 这些模板将结果计算为新的 ratio 类型。这种类型可以叫 type内嵌类型别名访问。例如, 下面的代码首先定义了两个代码 ratio 对象, 一个表示 1/60, 另一个表示 1/30。ratio_add将两个有理数相加的模板获得 result 有理数应在简化后进行 1/20。

using r1 = ratio<1,60>; using r2 = ratio<1,30>; using result = ratio_add<r1,r2>::type

C++标准还定义了一些 ratio 比较模板: ratio_equal、ratio_not_ equal、ratio_less、ratio_less_equal、ratio_greater和 ratio_greater_equal。与算术 ratio 模板一样,ratio 比较模板也是在编译时求值的。这些比较模板创建了一种新类型 std::bool_constant 来表示结果。bool_constant 也是 std::integral_constant,即 struct 模板,里面保存了一种类型和一个编译时常量值。例如,integral_constant保存了一个值为 15 的整型值。bool_constant 还是布尔类型的 integral_constant。 例如, bool_constant是 integral_constant, 存储值为 true 的布尔值。ratio比较模板的结果要么是 bool constant,要么是 bool_constant 。与 bool constant 或integral_constant 关联的值可通过 value 数据成员访问。下面的代码演示了 ratio_less 的使用。第 13 章讨论了如何使用 boolalpha 以 true 或 false 的形式输出布尔值:

using r1 = ratio<1,60>;
using r2 = ratio<1,30>;
using res = ratio_less<r2,r1>;
cout<<boolalpha<<res::value<< endl;

下面的例子整合了所有内容。注意,由于 ratio 是编译时常量,因此不能编写像 cout << rl 这样的代码,而是需要获得分子和分母并分别进行打印。

//Defaine a compile-time rational number
using r1 = ratio<1,60>;
cout<<"1) "<<r1::num<<"/"<<r1::den<<endl;

//get numerator end denominator
intmax_t num = r1::num;
intmax_t den = r1::den;
cout<<"2) "<<num<<"/"<<den<<endl;

//Add two rational numbers
using r2 = ratio<1,30>;
cout<<"3) "<<r2::num<<"/"<<r2::den<<endl;

//Compare two rational numbers
using res = ratio_less<r2,r1>;
cout<<"5) "<<boolalpha<<res::value<<endl;

输出

xz@xiaqiu:~/study/test/test$ ./test1) 1/602) 1/603) 1/305) false

为方便起见,ratio 库还提供了一些 SI(国际单位制)的类型别名,如下所示:

using yocto = ratio<1, 1'000'000'000'000'000'000'000'000>; // *
using zepto = ratio<1, 1'000'000'000'000'000'000'000>; // *
using atto = ratio<1, 1'000'000'000'000'000'000>;
using femto = ratio<1, 1'000'000'000'000'000>;
using pico = ratio<1, 1'000'000'000'000>;
using nano = ratio<1, 1'000'000'000>;
using micro = ratio<1, 1'000'000>;
using milli = ratio<1, 1'000>;
using centi = ratio<1, 100>;
using deci = ratio<1, 10>;
using deca = ratio<10, 1>;
using hecto = ratio<100, 1>;
using kilo = ratio<1'000, 1>;
using mega = ratio<1'000'000, 1>;
using giga = ratio<1'000'000'000, 1>;
using tera = ratio<1'000'000'000'000, 1>;
using peta = ratio<1'000'000'000'000'000, 1>;
using exa = ratio<1'000'000'000'000'000'000, 1>;
using zetta = ratio<1'000'000'000'000'000'000'000, 1>; // *
using yotta = ratio<1'000'000'000'000'000'000'000'000, 1>; // *

只有在编译器能表示类型别名为 intmax t 的常量分子和分母值时, 才会定义结尾处标了星号的 SI 单位。本章后面讨论 duration 时会给出这些预定义 SI 单位的使用示例。

chrono 库

chrono 库是一组操作时间的库。这个库包含以下组件;

持续时间

时钟

时点

所有组件都在 std::chrono 名称空间中定义,而且需要包含头文件。下面讲解每个组件。

持续时间

持续时间(duration)表示的是两个时间点之间的间隔时间,通过模板化的 duration 类来表示。duration 类保存了滴答数和滴答周期(tick period)。滴答周期指的是两个滴答之间的秒数,是一个编译时 ratio 常量,也就是说可以是 1 秒的分数。duration 模板接收两个模板参数,定义如下所示:

template<class Rep,class Period = ratio<1>> class duration{ 
       ...}

第一个模板参数 Rep 表示保存滴答数的变量类型,应该是一种算术类型,例如 long 和 double 等。第二个模板参数 Period 是表示滴答周期的有理数常量。如果不指定滴答周期,那么会使用默认值 ratio<1>,也就是说默认滴答周期为 1 秒。

duration 类提供了 3 个构造函数: 一个是默认构造函数,另一个构造函数接收一个表示滴答数的值作为参数; 第三个构造函数接收另一个 duration 作为参数。后者可用于将一个 duration 转换为另一个 duration,例如将分钟转换为秒。本节后面会列举一个示例。

duration 支持算术运算,例如+、-、*、/、%、++、–、+=、-=、*=、/=和%=,还支持比较运算符。duration类包含多个方法,如表 20-1 所示。

方法 说明
Rep count() const 以滴答数返回 duration 值,返回类型是 duration 模板中指定的类型参数
static duration zero() 返回持续时间值等于 0 的 duration
static duration min() static duration max() 返回 duration 模板指定的类型参数表示的最小值/最大值持续时间的 duration 值

C++17 添加了用于持续时间的 foor()、ceil()、round()和 abs()操作,行为与用于数值数据时类似。下面看一下如何在实际代码中使用 duration。每一个滴答周期为 1 秒的 duration 定义如下所示:

duration<long> d1;

由于 ratio<1>是默认的滴答周期,因此这行代码等同于:

duration<long,ratio<1>> d1;

下面的代码指定了滴答周期为 1 分钟的 duration(滴答周期为 60 秒):

duration<double,ratio<60>> d2;

下面的代码定义了每个滴答周期为 1/60 秒的 duration;

duration<double,ration<1,60>> d3;

根据本章前面的描述,头文件定义了一些 SI 有理数常量。这些预定义的常量在定义滴答周期时非常方便。例如,下面这行代码定义了每个滴答周期为 1 毫秒的 duration:

duration<long long,milli> d4;

下面的例子展示了 duration 的几个方面。它展示了如何定义 duration,如何对 duration 执行算术操作,以及如何将一个 duration 转换为另一个滴答周期不同的 duration:

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
using namespace std::chrono;

int main()
{ 
       
    //Specify a duration where each tick is 60 seconds
    duration<long,ratio<60>> d1(123);
    cout<<d1.count()<<endl;
    // Specify a duration represented by a double with each tick
    // equal to 1 second and assign the largest possible duration to it.
    duration<double> d2;
    d2 = d2.max();
    cout<<d2.count()<<endl;

    // Define 2 durations:
    // For the first duration, each tick is 1 minute
    // For the second duration, each tick is 1 second
    duration<long,ratio<60>> d3(10);  // = 10 minutes
    duration<long,ratio<1>> d4(14);  // = 14 seconds
    // Compare both durations
    if(d3 > d4)
        cout<<"d3 > d4"<<endl;
    else
        cout<<"d3 <= d4"<<endl;
    // Increment d4 with 1 resulting in 15 seconds
    ++d4;
    // Multiply d4 by 2 resulting in 30 seconds
    d4 *= 2;

    // Add both durations and store as minutes
    duration<double,ratio<60>> d5 = d3 + d4;

    //Add both durations and store as seconds
    duration<long,ratio<1>> d6 = d3 + d4;
    cout << d3.count() << " minutes + " << d4.count() << " seconds = "
        << d5.count() << " minutes or "
        << d6.count() << " seconds" << endl;
    // Create a duration of 30 seconds
    duration<long> d7(30);
    // Convert the seconds of d7 to minutes
    duration<double,ratio<60>> d8(d7);
    cout<<d7.count()<<" seconds = "<<d8.count() <<" minutes"<<endl;
    return 0;
}

输出

xz@xiaqiu:~/study/test/test$ ./test1231.79769e+308d3 > d410 minutes + 30 seconds = 10.5 minutes or 630 seconds30 seconds = 0.5 minutesxz@xiaqiu:~/study/test/test$ 

注意

上面输出中的第二行表示类型 double 能表示的最大 duration。具体的值因编译器而异。特别注意下面两行;

duration<double,ratio<60>> d5 = d3 + d4;duration<long,ratio<1>> d6 = d3 + d4;

这两行都计算了 d3+d4,但第一行将结果保存在表示分钟的浮点值中,第二行将结果保存在表示秘的整数中。分钟到秒的转换(或秒到分钟的转换)自动进行。上例中的下面两行展示了如何在不同时间单位问进行显式转换,

duration<long> d7(30); //secondsduration> d8(d7); //minutes 

第一行定义了一个滴答周期为 30 秒的 duration。第二行将这 30 秒转换为 0.5 分钟。使用这个方向的转换可能会得到非整数值,因此要求使用浮点数类型表示的 duration,否则会得到一些很诡异的编译错误。例如,下面的代码不能成功编译,因为 d8 使用了 long 类型而不是浮点类型:

duration<long>d7(30); //secondsduration> d8(d7); //minutes //Error!

但可以使用 duration_cast()进行强制转换,

duration<long> d7(30); //secondsauto d8 = duration_cast>>(d7); //minutes

此处,d8 的滴答周期为 0 分钟,因为整数除法用于将 30 秒转换为分钟数。在另一个方向的转换中,如果源数据是整数类型,那么不要求转换至浮点类型。因为如果从整数值开始转换,那么得到的总是整数值。例如,下面的代码将 10 分钟转换为秒数,两者都用整数类型 long 表示,

duration<long,ratio<60>> d9(10); //minutesduration d10(d9); //seconds

chrono 库还提供了以下标准的 duration 类型,它们位于 std::chrono 名称空间中:

using nanoseconds = duration<X 64 bits, nano>;using microseconds = duration<X 55 bits, micro>;using milliseconds = duration<X 45 bits, milli>;using seconds = duration<X 35 bits>;using minutes = duration<X 29 bits, ratio<60>>;using hours = duration<X 23 bits, ratio<3600>>;

区的具体类型取决于编译器,但 C++标准要求蕊的类型为至少指定大小的整数类型。上面列出的类型别名使用了本章前面描述的预定义的 SI ratio 类型别名。使用这些预定义的类型,不是编写:

duration<long,ratio<60>> d9(10); //minutes

而是编写;

minutes d9(10); //minutes

下面是一个讲解如何使用这些预定义 duration 的例子。这段代码首先定义了一个变量t,这个变量保存的是1 小时+23 分钟+45 秒的结果。这里使用了 auto 关键字,让编译器自动推导出 t 的准确类型。第二行使用了预定义的 seconds duration 构造函数,将t 的值转换为秒数,并将结果输出到控制台:

auto t = hours(1) + minutes(23)+seconds(45);cout<<seconds(t).count()<<" seconds"<<endl;

由于 C++标准要求预定义的 duration 使用整数类型,因此如果转换后得到非整数的值,那么会出现编译错误。尽管整数除法通常都会截断,但在使用通过 ratio 类型实现的 duration 时,编译器会将所有可能导致非零余数的计算声明为编译时错误。例如,下面的代码无法编译,因为转换 90 秒后得到的是 1.5 分钟:

seconds s(90);minutes m(s);

下面的代码也无法成功编译,即使 60 秒刚好为 1 分钟也是如此。这段代码也会产生编译错误,因为从秒到分钟的转换可能会产生非整数值:

seconds s(60);minutes m(s);

另一个方向的转换可正常完成,因为 minutes duration 是整数值,将它转换为 seconds 总能得到整数值;

minutes m(2);seconds s(m);

可使用标准的用户自定义字面量“h”“min”“s”“ms”“us”和“ns”来创建 duration。从技术角度看,这些定义在 std::literals::chrono_literals 名称空间中, 但也可通过 using namespace std::chrono 来访问。下面是一个示例:

using namespace std::chrono;//..auto myDuration = 43min;//42 minutes

时钟

clock 类由time_point 和 duration 组成。 time_point 类在 20.2.3 节中详细讨论,不过理解 clock 的工作方式并不需要这些细节。但由于 time_point 本身依赖于 clock,因此应该首先详细了解 clock 的工作方式。C++标准定义了 3 个 clock。第一个称为 system_clock,表示来自系统实时时钟的真实时间。第二个称为steady_clock,是一个能保证其 time_point 绝不递减的时钟。system_clock 无法做出这种保证,因为系统时钟可以随时调整。第三个称为high_resolution_clock,这个时钟的滴答周期达到了最小值。high_resolution_clock 可能就是 stead_clock 或 system_clock 的别名,具体取决于编译器。每个 clock 都有一个静态的 now()方法,用于把当前时间用作 time_point。system_clock 定义了两个静态辅助函数,用于 time_point 和 C 风格的时间表示方法 time_t之间的相互转换。第一个辅助函数为to_time_t(),它将给定 time_point 转换为 time_t,第二个辅助函数为 from_time_t(),它返回用给定 time_t 初始化的 time_ point。time_t 类型在头文件中定义。

下例展示了一个完整程序, 它从系统获得当前时间, 然后将这个时间以可供用户读取的格式输出到控制台。localtime()函数将 time_t 转换为用 tm 表示的本地时间,定义在头文件中。C++的 put_time()流操作算子定义在头文件中,详见第 13 章的讨论。

//Get current time as a time_pointsystem_clock::time_point tpoint = system_clock::now();//Convert to a time_ttime_t tt = system_clock::to_time_t(tpoint);//Convert to local timetm* t = localtime(&tt);//Write the time to the consolecout<

如果想要将时间转换为字符串,可使用 std::stringstream 或 C 风格的 strfime()函数,这些定义在中。

//Get current time as a time point
system_clock::time_point tpoint = system_clock::now();
//Convert to a time_t 
time_t tt = system_clock::to_time_t(tpoint);
//Convert to local time
tm* t = localtime(&tt);
//Convert to readable format
char buffer[80] = { 
       0};
strftime(buffer,sizeof(buffer),"%H:%M:%S",t);
//Write the time to the console
cout<<buffer<<endl;

通过 chrono 库还可计算一段代码执行所消耗的时间。下例展示了这个过程。变量 start 和 end 的实际类型为system_clock::time_point,diff企的实际类型为 duration:

//Get the start time
auto start = high_resolution_clock::now();
//Execute code that you want to time
double d = 0;
for(int i = 0;i < 1000000; ++i)
{ 
       
	d+=sqrt(sin(i) * cos(i));
}
//Get the end time and calculate the difference
auto end = high_resolution_clock::now();
auto diff = end - start;
//Convert the difference into milliseconds and output to the console
cout<<duration<double,milli>(diff).count()<<"ms"<<endl;

输出

xz@xiaqiu:~/study/test/test$ ./test88.7702msxz@xiaqiu:~/study/test/test$ ./test90.009msxz@xiaqiu:~/study/test/test$ ./test93.3066msxz@xiaqiu:~/study/test/test$ ./test88.8442msxz@xiaqiu:~/study/test/test$ ./test88.889msxz@xiaqiu:~/study/test/test$ 

这个例子中的循环执行一些算术操作,例如 sqrt()、sin()和 cos(),确保循环不会太快结束。如果在系统上获得非常小的毫秒差值,那么这些值不会很准确,应该增加循环的友代次数,使循环执行时间更长。小的计时值不会很准确,因为尽管定时器的精度为毫秒级,但在大多数操作系统上,这个定时器的更新频率不高,例如每10 毫秒或 15 毫秒更新一次。这会导致一种称为门限错误(gating erron)的现象,也就是说,任何持续时间小于1个定时器滴答的事件只花了 0 个时间单位,任何持续时间在 1 个和 2 个定时器滴答之间的事件花了 1 个时间单位。例如, 在一个定时器更新频率为 15 毫秒的系统上,运行了 44 毫秒的循环看上去只花了 30 毫秒。当使用这类定时器确定计算所用的时间时, 一定要确保整个计算消耗了大量的基本定时器滴答单位, 这样误差才能最小化。

时点

time_point 类表示的是时间中的某个时点,存储为相对于纪元(epoch)的 duration。time_ point 总是和特定的clock 关联,纪元就是所关联 clock 的原点。例如,经典 UNIX/Linux 的时间纪元是 1970年 1月 1 日,duration用秒来度量。Windows 的纪元是 1601 年 1月 1 日,duration 用 100 纳秒作为单位来度量。其他操作系统还有不同的纪元日期和 duration 单位。time_point 类包含 time_ since_epoch()函数, 它返回的 duration 表示所关联 clock 的纪元和保存的时间点之间的时间。C++支持合理的 time_point 和 duration 算术运算。下面列出这些运算。tp 代表 time point,d 代表 duration。

tp + d = tp tp – d = tpd + tp = tp tp – tp = dtp += d tp -= d

C++不支持的操作示例是 tp+tp。C++支持使用比较运算符来比较两个时间点,提供了两个静态方法: min()返回最小的时间点,而 max()返回最大的时间点。time_point 类有 3 个构造函数。e time_point(): 构造一个time_point, 通过 duration::zero()进行初始化。得到的 time_point 表示所关联 clock的纪元。

time_point(const duration&c ): 构造一个 time_point,通过给定的 duration 进行初始化。得到的 time_point 表示纪元+d。template time_point(const time_point& b: 构造一个 time_point,通过time_since_epoch()进行初始化。每个 time_point 都关联一个 clock。创建 time_point 时,指定 clock 作为模板参数:

time_point<steady_clock> tp1;

每个 clock 都知道各自的 time_point 类型,因此可编写以下代码;

steady_clock::time_point tp1;
//Create a time_point representing the epoch//of the associated steady clocktime_point tp1;//Add 10 minutes to the time_pointtp1 += minites(10);//Store the duration between epoch and time_pointauto d1 = tp1.time_since_epoch();//Convert the duration to seconds and output to the consoleduration d2(d1);cout<

输出

xz@xiaqiu:~/study/test/test$ ./test600 secondxz@xiaqiu:~/study/test/test$ ./test600 secondxz@xiaqiu:~/study/test/test$ ./test600 secondxz@xiaqiu:~/study/test/test$ 

可通过隐式方法或显式方法转换 time_point,这与 duration 转换类似。下面是一个隐式转换示例,输出是42000 ms。

time_point<steady_clock,seconds> tpSeconds(42s);//Convert seconds to milliseconds implicitytime_point tpMilliseconds(tpSeconds);cout<

生成随机数

在软件中生成符合需求的随机数是一个复杂主题。 在 C++11 之前, 生成随机数的唯一方法是使用 C 风格的srand()和 rand()函数。srand()函数需要在应用程序中调用一次,这个函数初始化随机数生成器,也称为设置种子(seeding)。通常应该使用当前系统时间作为种子。

警告;

在基于软件的随机数生成器中, 一定要使用高质量的种子。如果每次都用同一个种子初始化随机数生成器,那么每次生成的随机数序列都是一样的。这也是为什么通常要采用当前系统时间作为种子的原因。

​ 初始化随机数生成器后,通过 rand()生成随机数。下例展示了如何使用 srand()和 rand()。time(nullpt)调用返

srand(static_cast<unsigned int>(time(nullptr)));
cout<<rand()<<endl;

可通过以下函数生成特定范围内的随机数:

旧式的 C 风格 rand()函数生成的随机数在 0 到RAND_MAX 之间,根据标准,RAND_MAX 至少应该为32767。但rand()的低位通常不是非常随机,也就是说,通过上面的 getRandom()生成的随机数范围较小(例如 1和6之间),并且得到的随机性并不是非常好。

注意:

基于软件的随机数生成器永远都不可能生成真正的随机数,而是根据数学公式生成随机的效果,因此称为伪随机数生成器。

旧式的 srand()和 rand()函数没有提供太多灵活性。例如,无法改变生成的随机数的分布。C++11 添加了一个非常强大的库,能根据不同的算法和分布生成随机数。这个库定义在头文件中。这个库有 3 个主要组件: 随机数引擎(engine)、随机数引擎适配器(engine adapte)和分布(distribution)。随机数引擎负责生成实际的随机数, 并将生成后续随机数需要的状态保存起来。分布判断生成随机数的范围以及随机数在这个范围内的数学分布情况。随机数引擎适配器修改相关联的随机数引擎生成的结果。强烈建议不再使用 srand()和 rand(),而使用中的类。

随机数引擎

以下随机数引擎可供使用:

random_device

linear_congruential_engine

mersenne_twister_engine

Subtract_with_carry_engine

random_device 引擎不是基于软件的随机数生成器; 这是一种特殊引擎,要求计算机连接能真正生成不确定随机数的硬件,例如通过物理原理来生成。一种经典的机制是通过计算每个时间间隔内的 alpha 粒子数或类似的方法来测量放射性同位素的衰变,但还有很多其他类型的基于物理原理的生成器,包括测量反向偏压二极管“噪声”的方法(不必担心计算机内有放射源)。由于篇幅受限,本书不讲解这些机制的细节。根据 random_device 的规范,如果计算机没有连接此类硬件设备,这个库可选用一种软件算法。算法的选择取决于库的设计者。

随机数生成器的质量由随机数的熵(entropy)决定。 如果 random_device 类使用的是基于软件的伪随机数生成器,那么这个类的 entropy()方法返回的值为 0.0; 如果连接了硬件设备,则返回非零值。这个非零值是对连接设备的熵的估计。random_device 引擎的使用非常简单;

random_device rnd;
cout<<"Entropy: "<<rnd.entropy()<<endl;
cout<<"Min value: "<<rnd.min()
	<<", Max value: "<<rnd.max()<<endl;
cout<<"Random number: "<<rnd()<<endl;

这个程序的输出如下所示;

xz@xiaqiu:~/study/test/test$ ./test
Entropy: 0
Min value: 0, Max value: 4294967295
Random number: 3393511293

random_ device 的速度通常比伪随机数引擎更慢。因此,如果需要生成大量的随机数,建议使用伪随机数引擎,使用 random_device 为随机数引擎生成种子。除了 random_device 随机数引擎之外,还有 3 个伪随机数引擎线性同余引擎(linear congruential engine)保存状态所需的内存量最少。状态是一个包含上一次生成的随机数的整数,如果尚未生成随机数,则保存的是初始种子。这个引擎的周期取决于算法的参数,最高可达 2“,但是通常不会这么高。因此,如果需要使用高质量的随机数序列,那么不应该使用线性同

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章