当前课程知识点:基于Linux的C++ > 第十一讲 泛型编程 > 11.16 泛型编程实践(八) > 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 序列也被我们列出来了
这就是素数枚举这个实现
给我们提供的一个解决方案
看上去非常非常有趣 很神奇
就能在编译期完成全部的
素数的计算和枚举
-1.1 提纲
-1.2 程序设计的基本概念
-1.3 简单C/C++程序介绍
-1.4 程序设计的基本流程
-1.5 基本语法元素
-1.6 程序设计风格
-1.7 编程实践
-第一讲 C/C++基本语法元素--编程实践提交入口
-2.1 提纲
-2.2 结构化程序设计基础
-2.3 布尔数据
-2.4 分支结构
-2.5 break语句
-2.6 循环结构
-2.7 编程实践
-第二讲 程序控制结构--编程实践提交入口
-3.1 提纲
-3.2 函数声明、调用与定义
-3.3 函数调用栈框架
-3.4 编程实践
-第三讲 函数--编程实践提交入口
-4.1 提纲
-4.2 算法概念与特征
-4.3 算法描述
-4.4 算法设计与实现
-4.5 递归算法(一)
-4.6 递归算法(二)
-4.7 容错与计算复杂度
-4.8 编程实践
-第四讲 算法--编程实践提交入口
-5.1 提纲
-5.2 库与接口
-5.3 随机数库(一)
-5.4 随机数库(二)
-5.5 作用域与生存期
-5.6 典型软件开发流程(一)
-5.7 典型软件开发流程(二)
-5.8 编程实践
-第五讲 程序组织与开发方法--编程实践提交入口
-6.1 提纲
-6.2 字符
-6.3 数组(一)
-6.4 数组(二)
-6.5 结构体
-6.6 编程实践
-第六讲 复合数据类型--编程实践提交入口
-7.1 提纲
-7.2 指针基本概念
-7.3 指针与函数
-7.4 指针与复合数据类型(一)
-7.5 指针与复合数据类型(二)
-7.6 字符串
-7.7 动态存储管理(一)
-7.8 动态存储管理(二)
-7.9 引用
-7.10 编程实践
-第七讲 指针与引用--编程实践提交入口
-8.1 提纲
-8.2 数据抽象(一)
-8.3 数据抽象(二)
-8.4 链表(一)
-8.5 链表(二)
-8.6 链表(三)
-8.7 链表(四)
-8.8 函数指针(一)
-8.9 函数指针(二)
-8.10 抽象链表(一)
-8.11 抽象链表(二)
-8.12 编程实践
-第八讲 链表与程序抽象--编程实践提交入口
-9.1 提纲
-9.2 程序抽象与面向对象
-9.3 类类型
-9.4 对象(一)
-9.5 对象(二)
-9.6 类与对象的成员(一)
-9.7 类与对象的成员(二)
-9.8 类与对象的成员(三)
-9.9 继承(一)
-9.10 继承(二)
-9.11 继承(三)
-9.12 多态(一)
-9.13 多态(二)
-9.14 编程实践
-第九讲 类与对象--编程实践提交入口
-10.1 提纲
-10.2 四则运算符重载(一)
-10.3 四则运算符重载(二)
-10.4 关系与下标操作符重载
-10.5 赋值操作符重载(一)
-10.6 赋值操作符重载(二)
-10.7 赋值操作符重载(三)
-10.8 赋值操作符重载(四)
-10.9 赋值操作符重载(五)
-10.10 流操作符重载(一)
-10.11 流操作符重载(二)
-10.12 流操作符重载(三)
-10.13 操作符重载总结
-10.14 编程实践
-第十讲 操作符重载--编程实践提交入口
-11.1 提纲
-11.2 泛型编程概览
-11.3 异常处理机制(一)
-11.4 异常处理机制(二)
-11.5 运行期型式信息(一)
-11.6 运行期型式信息(二)
-11.7 模板与型式参数化
-11.8 题外话:术语翻译
-11.9 泛型编程实践(一)
-11.10 泛型编程实践(二)
-11.11 泛型编程实践(三)
-11.12 泛型编程实践(四)
-11.13 泛型编程实践(五)
-11.14 泛型编程实践(六)
-11.15 泛型编程实践(七)
-11.16 泛型编程实践(八)
-11.17 泛型编程实践(九)
-11.18 泛型编程实践(十)
-11.19 编程实践
-第十一讲 泛型编程--编程实践提交入口
-12.1 提纲
-12.2 程序执行环境(一)
-12.3 程序执行环境(二)
-12.4 程序执行环境(三)
-12.5 程序执行环境(四)
-12.6 输入输出(一)
-12.7 输入输出(二)
-12.8 文件系统
-12.9 设备
-12.10 库(一)
-12.11 库(二)
-12.12 makefile文件(一)
-12.13 makefile文件(二)
-12.14 makefile文件(三)
-12.15 编程实践
-第十二讲 Linux系统编程基础--编程实践提交入口
-13.01 提纲
-13.02 进程基本概念
-13.03 信号
-13.04 进程管理(一)
-13.05 进程管理(二)
-13.06 进程管理(三)
-13.07 进程间通信(一)
-13.08 进程间通信(二)
-13.09 进程间通信(三)
-13.10 进程间通信(四)
-13.11 进程池
-13.12 编程实践
-第十三讲 进程编程--编程实践提交入口
-14.1 提纲
-14.2 线程基本概念
-14.3 线程管理(一)
-14.4 线程管理(二)
-14.5 线程管理(三)
-14.6 线程管理(四)
-14.7 线程同步机制(一)
-14.8 线程同步机制(二)
-14.9 C++11线程库(一)
-14.10 C++11线程库(二)
-14.11 C++11线程库(三)
-14.12 C++11线程库(四)
-14.13 C++11线程库(五)
-14.14 编程实践
-第十四讲 线程编程--编程实践提交入口
-15.1 提纲
-15.2 Internet网络协议
-15.3 套接字(一)
-15.4 套接字(二)
-15.5 编程实践
-第十五讲 网络编程--编程实践提交入口