C++感受15-Hello STL 泛型启蒙

news2025/1/9 21:51:16
  • 生鱼片和STL的关系,你听过吗?
  • 泛型编程和面向对象编程,它们打架吗?
  • 行为泛型和数据泛型,各自的目的是?

泛型启蒙

0 楔

俄罗斯生鱼片,号称俄罗斯版的中国烤鸭,闻名于世。其鱼肉,源于北极海域,传统上用白鱼如omul、nelma 或 muksun 制作,传统上与伏特加搭配,加冰食用,味道柔软、新鲜、冰爽。

虽然我隐约知道前苏联曾经有过一段人民生活物资贫溃的时期,但我还是不太清楚,26岁的Alexander Stepanov 是怎么吃生鱼片,吃到严重中毒,吃到住院,吃到精神恍惚,吃到在心中产生 STL 库的种子……
在这里插入图片描述

1 五段话

五段来自 STL之父 (Alexander Stepanov / 亚历山大・斯特潘诺夫)的话(摘自其多个采访会谈内容)。

  • 谈专业——

“我出生在前苏联的莫斯科。我曾求学于莫斯科国立大学,学习数学。但‘遗憾’的是,我从来都没能成为数学家”

  • 谈程序设计——

“程序设计就像同未理顺的复杂问题打的一场战斗,要打好这场战斗,数学首当其冲,几个世纪以来,数学的作用正在于此。如果将现在生动的数学体系作为实验证据,对于解决人类遇到的复杂性问题,数学还是最有效的”

  • 谈中国——

“中国是一个伟大的国家。曾有过许多伟大的数学家:秦九韶的《数书九章》就是古代数学中的经典……”

  • 谈为什么没成为数学家——

“我实在不能对Tamagawa算术、Coxeter群等一些纯数学的东西感到兴趣” “我想用数学干点实事……”

  • 谈数学专业对编程思想的影响——

“…… 1976年,又要说回到苏联了,我因为吃生鱼片得了严重的食物中毒而住院,在精神恍惚中,我忽然意识到并发的加法计算能力是基于加法是结合性的。同时,我意识到并发的减法运算是和半群结构类型有关联的,这就是最基本的重点:算法是定义于代数结构基础之上的。我又花了一些年头,意识到必须在正规公理上加入复杂性必要条件以扩展结构的概念,接着又花了15年之久才完成全面的架构。我相信迭代器理论是计算科学的中心就象环或Banach区间理论是数学的中心一样。每次当我找到一个算法时,我都要努力去寻求它所定义的结构基础。我想做的就是泛化地描述算法,并乐此不疲。我可以花一个月时间去精确地描述一个众所周知的算法的泛化表示……”

2 课堂视频

我们对 STL,对泛型编程的认识,就从 STL 之父的介绍说起……

C++感受16-Hello STL 泛型启蒙

3 泛型函数

3.1 语法

template <typename T /* 类型参数列表 */>
函数定义

其中函数定义中,可将 T 视为类型名字,可用在函数返回值类型、函数参数、函数体内,比如:

template <typename T>
T add(T a, T b)
{
   return a + b;  // 相加结果的类型需为 T ,或能隐式转换为 T
}

类型参数列表和函数参数类似,可以有多个参数:

// 一个双类型参数的函数模板,可用于输出带标题的值
tempate <typename S, typename V>
void OutputWithTitle( S const& title, V const& value)
{
    std::cout << title << " : " << value;
}

3.2 使用

调用者通常通过精准地指定参数类型,为编译器提供准确的类型参数,由编译器在代码需要处,使用明确的类型,替换模板中对应的类型参数(比如上述示例中的 T、S 、V),在编译器自动生成实际函数。由“函数模板”生成的函数,需要时,我们会称它为 “模板函数”。

  • 例一、add 使用
// 调用1, 生成 int add (int a, int b) ...
auto r1 = add (1, 2); // auto 是 C++11 的新语法,此处编译器可推出其为 int 

// 调用2, 生成 double add (double a, double b) ...
double r2 = add (1.1, 2.0); // 此处的 2.0 不能写成 2

// 调用3, 生成 std::string add(std::string a, std::string b) ...
std::string r3 = add(
             	std::string("Hello "), // 明确的std::string 类型
		std::string("STL") // 同上
           ); // 将返回 "Hello STL"

上述代码将生成三个版本的 add 函数。
其中,r2 例中,如果 2.0 写成 2, 将造成编译器无法判定原模板中唯一的类型参数 T 的实际类型。r3 例中,如果直接传递纯C风格字符串(即字符串祼指针)"Hello " 和 “STL”,将造成编译失败,因为祼的字符串指针,不支持 “相加” 操作。

  • 例二、OutputWithTitle 使用
OutputWithTitle("姓名", "丁小明"); // S和V都是 char const* 类型

std::string title = "积分";
double value = 2999.052;
OutputWithTitle(title, value); // S→std::string,V→double;

3.3 auto: C++20 的骚操作

到了 C++20 新标准,一些简单的函数模板定义,可以使用 auto 来简化写法。

比如,经典写法:

template <typename T>
void foo(T v)
{
  // ...
}

在 20 新标下,可写作:

void foo(auto v)
{
   // ...
}

注意,如果需要多个参数模板,而每个参数模板都使用 auto 限定类型的话,此时,auto 并不执行 “同一类型” 的限定。比如:

// a , b 都是 auto,但并不存在类型必须一致的限定
auto add(auto a, auto b)
{
    return a + b;
}

// 以下调用成立
auto r = add(900, 99.99); // 返回 999.99

看起来很自由,但如此 “挣脱类型的束缚”,反倒容易滋生程序逻辑错误的恶果。

4. 泛型数据

有些时候——哪怕不是数学家——我们也会更加关心一个类型内部的组成结构,而不太关心(或者可以相对放心地忽略)数据的内部组成的类型。

比如,一个数学软件,可能希望有个类型,可以表示二维笛卡尔坐标(Cartesian coordinates)中的二维直角坐标点,不关心其中的x, y 坐标采用哪种类型。

像“不关心”,或 “随便”这样的用语,记得要反过来理解,它们的真实意思不是真的不在乎,面是:“干嘛分这分那?我都要!”。

template <typename T>
struct Point
{
   T x, y; // 用 T 表示 x, y 将来的真实类型,二者一致
};

和函数模板自动推理出函数略有不同,实际使用中,类的模板转换成类时,通常需要在类(class或struct)之后带上 ,其中 T 用于明确指定所需模板参数的真实类型。

// 使用 int 实例化类模板
Point<int> p1; 
p1.x = 10// 可将 float 赋 .y,只是转换过程中小数位会丢失
p1.y = 9.8f; 

// 使用 float 实例
Point<float> p2; 
p2.x = 99.f;
p2.y = 100; // 同样,此时不存在语法错误

以上示例中,之所以能跨类型赋值,是因为此时无论 p1 还是 p2 ,内部的 x, y 的类型都已经明确指定了。

定义类模板时,各类型参数既可用于指定成员数据的类型,也可用在成员方法(包括构造、析构等)身上(返回值、入参、函数内临时变量定义等等):

template <typename T>
struct Point
{
   T x, y;
   Point() = default; // 默认构造 C++11语法

   Point(T x, T y)
      : x(x), y(y) // 成员数据初始化列表
   {}


   // 让 x, y 各自增加指定的长度
   void IncBy (T dx, T dy)
   {
      x += dx;
      y += dy;
   }
};

构造、成员数据初始化列表知识点,讲 见《Hello Object 封装版.下》 等课堂。

注意,Point的第二个构造函数,和 IncBy() 成员函数都用到了 T,它就是定义类模板时的那个T,无需在函数自身加 template <typename > 作定义。

继续本体不包含 ,当构造函数使用到所在类模板的类型参数,并且,构造对象时的入参,能明确的,完整地推后出类模板的所有模板参数,则从 17 新标开始,定义对象时,不显示指定类模板的类型入参(全部或部分),也是可行的,比如:

Point p3 (99, 100); // 相当于 Point<int>
Point p4 (99.0, 100.0); // 相当于 Point<double>
Point p5 (std::string("90"), std::string("12.345")); // 虽然奇怪,但也合法

我们当然举双手双脚反对 p5 的类型 Point<std::string> ,试想对它调用 IncBy(“哦”, “哈”)之后……

5 初窥 vector

vector 翻译为 “矢量”,它是 STL 中最经典的的一个数据容器类(模板)。请阅读以下示例代码,在下一节课一上课,我们就来完善它:

#include <vector>

using namespace std;

struct 鸡精 { void 忍耐() { cout << "欢迎品尝!\n"; } };
struct 象妖 { void 愤怒() { cout << "大胆!可笑!!\n"; } };

int main()
{   
   vector<鸡精> v1; // 鸡精专用瓶
   vector<象妖> v2; // 象妖专用瓶

   std::cout << "妖怪!我叫你一声,你可敢答应?\n";

   ...

   鸡精 sj1;
   v1.push_back(sj1);

   象妖 dx1;
   v2.push_back(dx1);

   ...
}

只是看可学不会编程哦,像 STL 这样的库更是需要动手实践。谁出题?有专业的,一线编程多年的同行为你出题,为你批改,欢迎到本课原发站 www.d2school.com 做作业,提问,扎实扎实踏出编程学习的每一步,看似“劳累”,但这么做的,你会获得更快的进步。

作业示例

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2274024.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LabVIEW轴承性能测试系统

本文介绍了基于LabVIEW的高效轴承性能测试系统的设计与开发。系统通过双端驱动技术实现高精度同步控制&#xff0c;针对轴承性能进行全面的测试与分析&#xff0c;以提高轴承的可靠性和寿命。 项目背景 随着工业自动化程度的提高&#xff0c;对轴承的性能要求越来越高。传统的…

(k8s)Flannel Error问题解决!

1.问题描述 书接上回&#xff0c;我们在解决kubectl不断重启的时候引入了Flannel 网络插件&#xff0c;但是一上来就报错&#xff0c; 2.问题解决 自己的思路&#xff1a;照例开始检查 1.先检查一下目前Flannel的pod kubectl get pods --all-namespaces 2.检查 Flannel的po…

CatLog的使用

一 CatLog的简介 1.1 作用 CAT&#xff08;Central Application Tracking&#xff09; 是基于 Java 开发的实时应用监控平台&#xff0c;为美团点评提供了全面的实时监控告警服务。 1.2 组成部分 1.2.1 Transaction 1.Transaction 适合记录跨越系统边界的程序访问行为&a…

深入Android架构(从线程到AIDL)_18 SurfaceView的UI多线程02

目录 2、 使用SurfaceView画2D图 范例一 设计GameLoop(把小线程移出来) 范例二 2、 使用SurfaceView画2D图 范例一 以SurfaceView绘出Bitmap图像设计SpriteView类别来实作SurfaceHolder.Callback接口首先来看个简单的程序&#xff0c;显示出一个Bitmap图像。这个图像就构…

【FlutterDart】 拖动边界线改变列宽类似 vscode 那种拖动改变编辑框窗口大小(11 /100)

【Flutter&Dart】 拖动改变 widget 的窗口尺寸大小GestureDetector&#xff5e;简单实现&#xff08;10 /100&#xff09; 【Flutter&Dart】 拖动边界线改变列宽并且有边界高亮和鼠标效果&#xff08;12 /100&#xff09; 上效果&#xff1a; 这个在知乎里找到的效果&…

tk GMV MAX素材范围投放指南

Product GMy Max素材范围说明 Product GMy Max能自动获取带有相关商品锚点链接&#xff08;无论是单个锚点还是多个锚点&#xff09;的视频&#xff0c;并将其用于推广特定商品的广告素材&#xff0c;前提是这些视频已经获得广告授权。然而&#xff0c;请注意&#xff0c;多个…

物联网无线芯片模组方案,设备智能化交互升级,ESP32-C3控制应用

无线交互技术的核心在于实现设备之间的无缝连接和数据传输。在智能家居系统中&#xff0c;各种智能设备如智能灯泡、智能插座、智能门锁等&#xff0c;都通过无线网络相互连接&#xff0c;形成一个互联互通的生态。 用户可以通过语音助手、手机APP或其他智能终端&#xff0c;远…

ubuntu为Docker配置代理

终端代理 我们平常在ubuntu终端中使用curl或git命令时&#xff0c;往往会很慢。 所以&#xff0c;首先需要给ubuntu终端环境添加代理。 查看自身那个软件的端口号&#xff0c;我这里是7890。 sudo gedit ~/.bashrcexport http_proxyhttp://localhost:7890 export https_pr…

【VUE 指令学习笔记】

v-bind :单向绑定解析表达式&#xff0c;可简写为:xxx v-model :双向数据绑定。 v-for&#xff1a;遍历数组/对象/字符串 v-on&#xff1a;绑定事件监听&#xff0c;可简写为。 v-if:条件渲染(动态控制节点是否存存在) v-else:条件渲染(动态控制节点是否存存在) v-show:条件渲染…

用OpenCV实现UVC视频分屏

分屏 OpencvUVC代码验证后话 用OpenCV实现UVC摄像头的视频分屏。 Opencv opencv里有很多视频图像的处理功能。 UVC Usb 视频类&#xff0c;免驱动的。视频流格式有MJPG和YUY2。MJPG是RGB三色通道的。要对三通道进行分屏显示。 代码 import cv2 import numpy as np video …

123.【C语言】数据结构之快速排序挖坑法和前后指针法

目录 1.挖坑法 执行流程 代码 运行结果 可读性好的代码 2.前后指针法(双指针法) 执行流程 单趟排序代码 将单趟排序代码改造后 写法1 简洁的写法 3.思考题 1.挖坑法 执行流程 "挖坑法"顾名思义:要有坑位,一开始将关键值放入临时变量key中,在数组中形成…

JavaFx 21 项目Markdown 预览、编辑、新建、文件树、删除、重命名

项目文件结构 项目的源代码和资源文件存放在以下路径: 源代码: src/main/java/com/kong/markdown/ 包含多个 Java 文件,主要实现了应用的功能: App.java:主类,可能包含应用的启动逻辑。FileService.java:可能与文件操作相关的服务类。MainController.java:控制器类,可…

jenkins入门6 --拉取代码

Jenkins代码拉取 需要的插件&#xff0c;缺少的安装下 新建一个item,选择freestyle project 源码管理配置如下&#xff1a;需要添加git库地址&#xff0c;和登录git的用户密码 配置好后执行编译&#xff0c;成功后拉取的代码在工作空间里

数学建模入门——建模流程

摘要&#xff1a;本文介绍了数学建模的一般流程概述。 目录 一、前言 二、数据预处理 三、描述性统计分析 四、模型建立 五、模型评价 一、前言 本文将为想要入门数学建模的同学讲述数学建模的一般流程。但数学建模流程并非一成不变。虽有大致步骤&#xff0c;像分析问题、…

《机器学习》集成学习之随机森林

目录 一、集成学习 1、简介 2、集成学习的代表 3、XGBoost和随机森林的对比 相同点&#xff1a; 不同点&#xff1a; 二、Bagging之随机森林 1、简介 2、随机森林的核心思想 3、随机森林生成步骤 4、随机森林的优点 5、随机森林的缺点 三、随机森林的代码实现 1、…

【HarmonyOS NEXT】鸿蒙应用实现屏幕录制详解和源码

【HarmonyOS NEXT】鸿蒙应用实现屏幕录制详解和源码 一、前言 官方文档关于屏幕录制的API和示例介绍获取简单和突兀。使用起来会让上手程度变高。所以特意开篇文章&#xff0c;讲解屏幕录制的使用。官方文档参见&#xff1a;使用AVScreenCaptureRecorder录屏写文件(ArkTS) 二…

CSS——22.静态伪类(伪类是选择不同元素状态)

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>静态伪类</title> </head><body><a href"#">我爱学习</a></body> </html>单击链接前的样式 左键单击&#xff08;且…

JavaWeb开发(六)XML介绍

1. XML介绍 1.1. 什么是XML &#xff08;1&#xff09;XML 指可扩展标记语言(EXtensible Markup Language)XML 是一种很像HTML的标记语言。   &#xff08;2&#xff09;XML 的设计宗旨是传输数据(目前主要是作为配置文件)&#xff0c;而不是显示数据。   &#xff08;3&a…

攻防世界 bug

发现有Register界面&#xff0c;先去注册 登录以后发现以下界面&#xff0c;点击Manage显示you are not admin&#xff0c;并且在注册界面用admin为注册名时显示用户名已存在。初步推测是设法改变admin的密码取得权限。 在主界面一通操作并没有什么发现&#xff0c;去findpw…

maven依赖的配置和排除依赖

1.依赖的配置 1.1位置&#xff1a;写在<properties></properties>标签之下&#xff0c;<project></project>里。 1.2语法固定的格式 <dependencies><dependency></dependency></dependencies> 1.3在 <dependency><…