当前课程知识点:基于Linux的C++ >  第十一讲 泛型编程 >  11.16 泛型编程实践(八) >  LinuxCPP1116

返回《基于Linux的C++》慕课在线视频课程列表

LinuxCPP1116在线视频

LinuxCPP1116

下一节:LinuxCPP1117

返回《基于Linux的C++》慕课在线视频列表

LinuxCPP1116课程教案、知识点、字幕

那么我们第二个例子就是素数枚举

这个例子也有点小怪 当然也很有趣

这个模板是这么写的

template〈int p, int i〉 struct PrimeMagicCube

{ enum { answer = p % i && PrimeMagicCube〈p, i-1〉::answer }; };

{ enum { answer = p % i && PrimeMagicCube〈p, i-1〉::answer }; };

over 这个类模板干嘛用的呢

它就是计算 p 是不是素数的

答案就是它是不是素数

如果是素数 答案就是 true

否则就是 false

当然因为它是个枚举文字值

所以如果它是素数 answer 就是 1

如果它不是素数 answer 就是 0

就是这个意思

那我们怎么算它是不是素数呢

我们就按照它的素数的定义

取的是什么东西呢

取的就是看它能不能够被

小于 p 的所有的数整除

所以这个模板本身的意思就是看 p

能不能够被所有不大于 i 的值所整除

其实就是这个意思 不是被 i 所整除

而是被所有不大于 i 的值整除

为什么呢 因为能够被 i 整除这个结论

已经写在这个地方了

p % i 如果余数不为 0

结果就是 true

它不就把 1 传进去了 对吧

因为这是个逻辑运算嘛

它余数不管是几

只要不为 0 它就相当于 true

它就做了这个逻辑运算

得到的结果就是 true 和 false

最后又会转型成 1 和 0

然后传给 answer 嘛

它后面还跟着一个递归呢

PrimeMagicCube〈p, i-1〉

它就看 p % (i - 1) 能不能整除

对吧 那想看 p、i - 1 的时候

你不还要体化这个版本吗

体化这个类模板吗

OK 继续体化

可是到看 p、i - 1 的时候

那你不还要再看 p、i - 2 嘛

它就是按照这个模式

不断地向下递归的

看看能不能够被整除

对吧 结论就是这个

这个结构名字 我称它为素数魔方

专门做的就是这个事儿

我们使用的是 struct 关键字 没使用 class

所以这个东西自动就是 public

我少写了一行代码

要不我这个片子排不下

我们可以对这个素数魔方类

进行部分特化 以提供它递归终止版本

这里面是双参数

我只特化它的一个参数

那不就是部分特化吗

提供它的终止条件

对于 p 这个参数来讲

我们不需要特化它 只需要对 i

当 i 递减到 1 的时候

我其实就可以停了 素性就出来了

对不对 所以我们部分特化它

template〈int p〉 struct PrimeMagicCube〈p, 1〉{ enum { answer = 1 }; };

template〈int p〉 struct PrimeMagicCube〈p, 1〉{ enum { answer = 1 }; };

true 一直递减到 1

都没有找到它的因子

都不能整除 answer 显然是 true

所以我们传 1 这个就是部分特化

现在我们想求给定值的素数

我们这里其实求的不是给定值

而是求不大于一个特定值的全部的素数

都打印出来

我们就定义一个类模板

template〈int i〉 struct Number

这就是一个数字模板

你传几它就是数字几

我们来看它是不是素数

有一个嵌套的对象在这里

Number〈i - 1〉 a

这是一个递归定义的数值对象

你想定义数值 n

那么在数值 n 里边就有一个成员

是数值 n - 1 的一个对象

在数值 n - 1 的那个里边

就有一个数值 n - 2 的一个对象

它是按照这个方式递归定义的

为什么呢 就是因为我们就要用它

来递归地去求所有的素数

当然它还有一个

enum{ answer = PrimeMagicCube〈i, i-1〉::answer }

enum{ answer = PrimeMagicCube〈i, i-1〉::answer }

就去求这个模板 PrimeMagicCube〈i, i-1〉

看它的 answer 是几

就初始化给我们这个 answer

什么意思 你给我传一个数 i

你问我 i 是不是素数

那我们就从 i - 1 开始除起

看 i - 1 能不能整除 不能

i - 2 能不能整除 不能

i - 3 能不能整除 不能

一直到除到 1(注:不包含 1 本身) 都不能

编译期就能算出来 answer 就是 1

在任意一个时刻 发现它能够整除

那么这个结论就会变成 0

那个 0 和任何量 “&&”

它结果不都是 0 嘛

对吧 都是 false 嘛

所以结果传 0 就进去了

我们提供一个函数 IsPrime()

问你是不是素数

我们实际上会把这个素数给打印出来

if( answer )

如果这个数值对象 你传的这个 i

这个数是个素数

那么 answer 它就会写 1 在那里边

那么我们就会输出这个素数

如果不是 我就不输出它

就是合数 我就不输出了

然后我递归调用 a.IsPrime()

为啥要递归调用呢

因为我要看 i - 1 那个数是不是素数

不是说了嘛

我要输出不大于 i 的全部素数

a.IsPrime() 它就会递归调用

去看 i - 1 是不是素数

如果是素数 它就把它打印出来

然后递归调用

怎么又递归调用了

你是在 a 这个对象上面调用的 IsPrime() 呀

不是 Number〈i〉 的那个对象

是 Number〈i - 1〉 的那个对象

你在 IsPrime() 里边调用了这个 a.IsPrime()

你调用的是 Number〈i - 1〉 的那个对象 a

在它上调用 IsPrime()

它就会执行 它打印 i - 1

它是不是素性的那个 answer

然后那个对象本身

不还有一个成员名字叫 a 嘛

你又在 Number〈i - 1〉 的那个成员 a

那个成员 a 是什么 是 Number〈i - 2〉 呀

又在那个上面调用了IsPrime()

它就把 i - 2 那个是不是素数的结论

它不又输出了嘛

它就按照这个方式

递归地输出了全部的素数

从大到小 你传我 100

第一个输出的就是 97

最后输出的才是 2 按这个模式输出

最后我再写一个输出我们的 answer 的值

我们的 answer 是什么呢

answer 不是 0 就是 1

最后不管三七二十一

我都把它的 1 或者 0 的值输出出来

你注意我这个递归调用

因为这个 a.IsPrime() 的递归调用

是写在 if( answer ) 的后边

写在后边一条输出语句的前边

所以全部素数的输出是从大到小排列的

但是从 2 开始的所有数的素性

是从小到大输出的

所以你看到

它最后的输出的结果那个序列

它是什么呢

1、1、0、1、0、1...

为什么是这个序列

就是 2 是素数 所以输出 1

3 是素数 所以输出 1

4 是合数 所以输出 0

5 是素数呀 所以输出 1

6 是合数 所以输出 0

7 是素数 所以输出 1...

就是这个格式 1、1、0、1、0、1...

按照这个格式

后面的当然是 0、0、0、1

对吧 你就会看到所有的这个素性

用一个 0、1 序列把它输出出来

如果你的程序可以处理这个 0、1 序列

这个素性

一个不大于 n 的这个值的全部的素性

你一下子就能够全得到

而这都是编译期做到的

我们的 Number 这个数值类

同样需要提供一个特化的版本

我们要特化 让它终止于 2

所以如果这个数到了素数 2

那么 answer 显然就是 1

输出的时候呢 就直接输出 2 的值

然后立即就开始输出它的 answer

就是递归到此就开始发生了变化

它就停止

我们的实现 我们 Number〈100〉 a

就是输出不大于 100 的全部的素数和它的素性的列表

就是输出不大于 100 的全部的素数和它的素性的列表

素数从大到小输出

素性列表 0、1 序列从小到大输出

当然终止只到 2

我们不看 1 的 就是 2 到 100 之间的

全部的素数给列出来了

2 到 100 的所有的数的素性

0、1 序列也被我们列出来了

这就是素数枚举这个实现

给我们提供的一个解决方案

看上去非常非常有趣 很神奇

就能在编译期完成全部的

素数的计算和枚举

基于Linux的C++课程列表:

第一讲 C/C++基本语法元素

-1.1 提纲

--LinuxCPP0101

-1.2 程序设计的基本概念

--LinuxCPP0102

-1.3 简单C/C++程序介绍

--LinuxCPP0103

-1.4 程序设计的基本流程

--LinuxCPP0104

-1.5 基本语法元素

--LinuxCPP0105

-1.6 程序设计风格

--LinuxCPP0106

-1.7 编程实践

--LinuxCPP0107

-第一讲 C/C++基本语法元素--编程实践提交入口

第二讲 程序控制结构

-2.1 提纲

--LinuxCPP0201

-2.2 结构化程序设计基础

--LinuxCPP0202

-2.3 布尔数据

--LinuxCPP0203

-2.4 分支结构

--LinuxCPP0204

-2.5 break语句

--LinuxCPP0205

-2.6 循环结构

--LinuxCPP0206

-2.7 编程实践

--LinuxCPP0207

-第二讲 程序控制结构--编程实践提交入口

第三讲 函数

-3.1 提纲

--LinuxCPP0301

-3.2 函数声明、调用与定义

--LinuxCPP0302

-3.3 函数调用栈框架

--LinuxCPP0303

-3.4 编程实践

--LinuxCPP0304

-第三讲 函数--编程实践提交入口

第四讲 算法

-4.1 提纲

--LinuxCPP0401

-4.2 算法概念与特征

--LinuxCPP0402

-4.3 算法描述

--LinuxCPP0403

-4.4 算法设计与实现

--LinuxCPP0404

-4.5 递归算法(一)

--LinuxCPP0405

-4.6 递归算法(二)

--LinuxCPP0406

-4.7 容错与计算复杂度

--LinuxCPP0407

-4.8 编程实践

--LinuxCPP0408

-第四讲 算法--编程实践提交入口

第五讲 程序组织与开发方法

-5.1 提纲

--LinuxCPP0501

-5.2 库与接口

--LinuxCPP0502

-5.3 随机数库(一)

--LinuxCPP0503

-5.4 随机数库(二)

--LinuxCPP0504

-5.5 作用域与生存期

--LinuxCPP0505

-5.6 典型软件开发流程(一)

--LinuxCPP0506

-5.7 典型软件开发流程(二)

--LinuxCPP0507

-5.8 编程实践

--LinuxCPP0508

-第五讲 程序组织与开发方法--编程实践提交入口

第六讲 复合数据类型

-6.1 提纲

--LinuxCPP0601

-6.2 字符

--LinuxCPP0602

-6.3 数组(一)

--LinuxCPP0603

-6.4 数组(二)

--LinuxCPP0604

-6.5 结构体

--LinuxCPP0605

-6.6 编程实践

--LinuxCPP0606

-第六讲 复合数据类型--编程实践提交入口

第七讲 指针与引用

-7.1 提纲

--LinuxCPP0701

-7.2 指针基本概念

--LinuxCPP0702

-7.3 指针与函数

--LinuxCPP0703

-7.4 指针与复合数据类型(一)

--LinuxCPP0704

-7.5 指针与复合数据类型(二)

--LinuxCPP0705

-7.6 字符串

--LinuxCPP0706

-7.7 动态存储管理(一)

--LinuxCPP0707

-7.8 动态存储管理(二)

--LinuxCPP0708

-7.9 引用

--LinuxCPP0709

-7.10 编程实践

--LinuxCPP0710

-第七讲 指针与引用--编程实践提交入口

第八讲 链表与程序抽象

-8.1 提纲

--LinuxCPP0801

-8.2 数据抽象(一)

--LinuxCPP0802

-8.3 数据抽象(二)

--LinuxCPP0803

-8.4 链表(一)

--LinuxCPP0804

-8.5 链表(二)

--LinuxCPP0805

-8.6 链表(三)

--LinuxCPP0806

-8.7 链表(四)

--LinuxCPP0807

-8.8 函数指针(一)

--LinuxCPP0808

-8.9 函数指针(二)

--LinuxCPP0809

-8.10 抽象链表(一)

--LinuxCPP0810

-8.11 抽象链表(二)

--LinuxCPP0811

-8.12 编程实践

--LinuxCPP0812

-第八讲 链表与程序抽象--编程实践提交入口

第九讲 类与对象

-9.1 提纲

--LinuxCPP0901

-9.2 程序抽象与面向对象

--LinuxCPP0902

-9.3 类类型

--LinuxCPP0903

-9.4 对象(一)

--LinuxCPP0904

-9.5 对象(二)

--LinuxCPP0905

-9.6 类与对象的成员(一)

--LinuxCPP0906

-9.7 类与对象的成员(二)

--LinuxCPP0907

-9.8 类与对象的成员(三)

--LinuxCPP0908

-9.9 继承(一)

--LinuxCPP0909

-9.10 继承(二)

--LinuxCPP0910

-9.11 继承(三)

--LinuxCPP0911

-9.12 多态(一)

--LinuxCPP0912

-9.13 多态(二)

--LinuxCPP0913

-9.14 编程实践

--LinuxCPP0914

-第九讲 类与对象--编程实践提交入口

第十讲 操作符重载

-10.1 提纲

--LinuxCPP1001

-10.2 四则运算符重载(一)

--LinuxCPP1002

-10.3 四则运算符重载(二)

--LinuxCPP1003

-10.4 关系与下标操作符重载

--LinuxCPP1004

-10.5 赋值操作符重载(一)

--LinuxCPP1005

-10.6 赋值操作符重载(二)

--LinuxCPP1006

-10.7 赋值操作符重载(三)

--LinuxCPP1007

-10.8 赋值操作符重载(四)

--LinuxCPP1008

-10.9 赋值操作符重载(五)

--LinuxCPP1009

-10.10 流操作符重载(一)

--LinuxCPP1010

-10.11 流操作符重载(二)

--LinuxCPP1011

-10.12 流操作符重载(三)

--LinuxCPP1012

-10.13 操作符重载总结

--LinuxCPP1013

-10.14 编程实践

--LinuxCPP1014

-第十讲 操作符重载--编程实践提交入口

第十一讲 泛型编程

-11.1 提纲

--LinuxCPP1101

-11.2 泛型编程概览

--LinuxCPP1102

-11.3 异常处理机制(一)

--LinuxCPP1103

-11.4 异常处理机制(二)

--LinuxCPP1104

-11.5 运行期型式信息(一)

--LinuxCPP1105

-11.6 运行期型式信息(二)

--LinuxCPP1106

-11.7 模板与型式参数化

--LinuxCPP1107

-11.8 题外话:术语翻译

--LinuxCPP1108

-11.9 泛型编程实践(一)

--LinuxCPP1109

-11.10 泛型编程实践(二)

--LinuxCPP1110

-11.11 泛型编程实践(三)

--LinuxCPP1111

-11.12 泛型编程实践(四)

--LinuxCPP1112

-11.13 泛型编程实践(五)

--LinuxCPP1113

-11.14 泛型编程实践(六)

--LinuxCPP1114

-11.15 泛型编程实践(七)

--LinuxCPP1115

-11.16 泛型编程实践(八)

--LinuxCPP1116

-11.17 泛型编程实践(九)

--LinuxCPP1117

-11.18 泛型编程实践(十)

--LinuxCPP1118

-11.19 编程实践

--LinuxCPP1119

-第十一讲 泛型编程--编程实践提交入口

第十二讲 Linux系统编程基础

-12.1 提纲

--LinuxCPP1201

-12.2 程序执行环境(一)

--LinuxCPP1202

-12.3 程序执行环境(二)

--LinuxCPP1203

-12.4 程序执行环境(三)

--LinuxCPP1204

-12.5 程序执行环境(四)

--LinuxCPP1205

-12.6 输入输出(一)

--LinuxCPP1206

-12.7 输入输出(二)

--LinuxCPP1207

-12.8 文件系统

--LinuxCPP1208

-12.9 设备

--LinuxCPP1209

-12.10 库(一)

--LinuxCPP1210

-12.11 库(二)

--LinuxCPP1211

-12.12 makefile文件(一)

--LinuxCPP1212

-12.13 makefile文件(二)

--LinuxCPP1213

-12.14 makefile文件(三)

--LinuxCPP1214

-12.15 编程实践

--LinuxCPP1215

-第十二讲 Linux系统编程基础--编程实践提交入口

第十三讲 进程编程

-13.01 提纲

--LinuxCPP1301

-13.02 进程基本概念

--LinuxCPP1302

-13.03 信号

--LinuxCPP1303

-13.04 进程管理(一)

--LinuxCPP1304

-13.05 进程管理(二)

--LinuxCPP1305

-13.06 进程管理(三)

--LinuxCPP1306

-13.07 进程间通信(一)

--LinuxCPP1307

-13.08 进程间通信(二)

--LinuxCPP1308

-13.09 进程间通信(三)

--LinuxCPP1309

-13.10 进程间通信(四)

--LinuxCPP1310

-13.11 进程池

--LinuxCPP1311

-13.12 编程实践

--LinuxCPP1312

-第十三讲 进程编程--编程实践提交入口

第十四讲 线程编程

-14.1 提纲

--LinuxCPP1401

-14.2 线程基本概念

--LinuxCPP1402

-14.3 线程管理(一)

--LinuxCPP1403

-14.4 线程管理(二)

--LinuxCPP1404

-14.5 线程管理(三)

--LinuxCPP1405

-14.6 线程管理(四)

--LinuxCPP1406

-14.7 线程同步机制(一)

--LinuxCPP1407

-14.8 线程同步机制(二)

--LinuxCPP1408

-14.9 C++11线程库(一)

--LinuxCPP1409

-14.10 C++11线程库(二)

--LinuxCPP1410

-14.11 C++11线程库(三)

--LinuxCPP1411

-14.12 C++11线程库(四)

--LinuxCPP1412

-14.13 C++11线程库(五)

--LinuxCPP1413

-14.14 编程实践

--LinuxCPP1414

-第十四讲 线程编程--编程实践提交入口

第十五讲 网络编程

-15.1 提纲

--LinuxCPP1501

-15.2 Internet网络协议

--LinuxCPP1502

-15.3 套接字(一)

--LinuxCPP1503

-15.4 套接字(二)

--LinuxCPP1504

-15.5 编程实践

--LinuxCPP1505

-第十五讲 网络编程--编程实践提交入口

课程文档

-课程PDF文件

LinuxCPP1116笔记与讨论

也许你还感兴趣的课程:

© 柠檬大学-慕课导航 课程版权归原始院校所有,
本网站仅通过互联网进行慕课课程索引,不提供在线课程学习和视频,请同学们点击报名到课程提供网站进行学习。