【c++指南】模板VS手写代码:这场效率对决你站哪边?【上】

【c++指南】模板VS手写代码:这场效率对决你站哪边?【上】


🌟 各位看官好,我是egoist2023

🌍 种一棵树最好是十年前,其次是现在!


🚀 今天来学习模板的相关知识,有了模板之后就能大大提高效率。

👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享给更多人哦!

目录

引入

泛型编程

函数模板

概念

格式

原理

函数模板实例化

匹配原则

引入

类模板

定义格式

类模板实例化


引入

泛型编程

在如上一段代码中,写了一个Swap函数,为了多种类型的支持,因此通过函数重载达到了多种类型的变量的交换。但是,如果此时增加一个新类型:如float类型或者类类型时,又需要程序员再增加自己对应的的函数。

  1. 这是非常麻烦且代码复用性较低。每当出现新类型,都需要手动增加新函数;
  2. 代码的维护性低,一旦某个位置出错,其余的函数重载都得改动。

很显然,这种方式不是我们所期望的。那能否告诉编译器一个模子,让编译器根据不同的类型利用该模子来生成代码呢?

泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。换句话说,有了函数重载的支持,才能达到模板的目的。模板相当于一个模具,通过这个模具能填充不同类型,生成不同的的类型的代码。如同在古代没有造纸术时,只能依赖刀具处理木牍、竹简生成对应的模具;但当蔡伦发明了造纸术后,有了抄纸的模具“帘床”,大大提高了效率。

函数模板

概念

函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。

格式

小编带着瞅瞅stl模板库中基本都是模板来实现的。

 从这里就可以发现模板的妙处,编译器能通过我们写的变量自动推导类型,生成不同的函数。那模板的格式又是咋样的呢?

        template<typename T1, typename T2,......,typename Tn>                         (typename也可写成class,大多情况上是没区别的)        返回值类型函数名(参数列表){}

//template<typename T> template<class T> void Swap(T& left, T& right) { T temp = left; left = right; right = temp; }

原理

函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器来做。在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此。

函数模板实例化

用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:隐式实例化和显式实例化。1. 隐式实例化:让编译器根据实参推演模板参数的实际类型

template<class T> T Add(const T& left, const T& right) { return left + right; } int main() { int a1 = 10, a2 = 20; double d1 = 10.0, d2 = 20.0; Add(a1, a2); Add(d1, d2); return 0; }

在上面的程序就是隐式实例化(编译器自动识别参数类型)的体现。那如果left是int类型,right是double类型呢?编译器又是否能够自动识别类型?

这里的报错信息是未找到匹配的重载函数,为什么呢?

因为在编译期间,当编译器看到该实例化时,需要推演其实参类型通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有一个T, 编译器无法确定此处到底该将T确定为int 或者 double类型而报错。(编译器一般不会对类型进行强转)那有什么解决方法呢?

  1. 自己手动强转从而来匹配对应的重载函数;
  2. 使用显式实例化。

2. 显式实例化:在函数名后的<>中指定模板参数的实际类型

Add<int>(a, b);

匹配原则

1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数。2.对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而 不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板。(即编译器有现成的会吃现成的,不会去推演)那如果非要调用模板函数呢?使用显式实例化。3. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换

前言

在实现顺序表、链表等数据结构时,我们常常使用

typedef + 类型 + 命名;

但是这种只能支持一种类型,即类型如果写成int,就只能插入int类型的数据;如果写成double类型,就只能插入double类型的数据。有没什么方法可以让这个类实现多个类型呢?那就需要引入类模板的概念。

类模板

定义格式

template<class T1, class T2, ..., class Tn> class 类模板名 { // 类内成员定义 };
// 类模版 template<class T> class Stack { public: Stack(size_t capacity = 4) { _array = new T[capacity]; _capacity = capacity; _size = 0; } void Push(const T& data) { //... } private: T* _array; size_t _capacity; size_t _size; };

在上面一段程序中,写了一段Stack类模板,可以支持多种类型的类。表面上看,我们只写了一份类模板,但实际编译器需要根据不同类型生成不同类型的类。这种方式极大减少了程序员敲代码的痛苦,脏活累活都交给了编译器来处理。

并且,模板并不推荐声明和定义分离到两个文件.h 和.cpp,这样会出现链接错误。(后续会讲)

类模板实例化

类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类

// Stack是类名,Stack<int>才是类型 Stack<int> st1; // int Stack<double> st2; // double

Read more

爆肝 2 天,用 GLM5 开发了 OpenClaw 接入微信 bot,已开源!

这是苍何的第 493 篇原创! 大家好,我是苍何。 OpenClaw,这个 GitHub 上 18 万 Star 的怪物级开源项目,你们应该都听过了吧? 飞书能接、钉钉能接、企业微信能接、QQ 能接、Discord 能接…… 但偏偏最多人用的「微信个人号」,它不支持。 我翻遍了 GitHub、掘金、知乎,找到的方案要么是企业微信绕一圈,要么是用微信 Web 协议搞,动不动就封号。 说实话,这谁顶得住? 天天在微信上跟朋友聊天、在群里吹水,结果想接个 OpenClaw 都这么费劲? 麻了。 于是我决定自己干。 「爆肝 2 天,我把 OpenClaw 接入了微信个人号,并且已经开源了。」 地址:

By Ne0inhk
Git 用户名与邮箱配置指南

Git 用户名与邮箱配置指南

前言 在使用 Git 进行版本控制时,每一次代码提交(commit)都会记录提交者的身份信息。这些信息不仅用于追踪代码变更历史,还在团队协作、代码审查和开源贡献中发挥着重要作用。 Git 通过 用户名(user.name) 和 邮箱(user.email) 来标识开发者身份。正确配置这两项信息,是使用 Git 的第一步,也是确保提交记录清晰、可追溯的关键。 一、为什么需要设置用户名和邮箱? Git 是一个分布式版本控制系统,它不依赖中央服务器来管理用户身份。因此,每个开发者必须在本地明确声明自己的身份。Git 会在每次执行 git commit 时,自动将 user.name 和 user.email 写入提交记录。 如果没有正确设置,可能会导致: * 提交记录显示为 unknown 或默认系统用户名;

By Ne0inhk

超详细 Git 讲解(通俗易懂 + 全面覆盖)

超详细 Git 讲解(通俗易懂 + 全面覆盖) 一、先搞懂:为什么需要 Git?(5 分钟) 先从大一同学能理解的场景切入,避免一上来就讲技术: * 场景 1:写代码改来改去,想回退到昨天的版本,却找不到旧文件; * 场景 2:实验室多人合作写项目,改同一个文件互相覆盖,代码越改越乱; * 场景 3:想同时开发两个功能(比如 “登录功能” 和 “注册功能”),改了登录的代码,注册的代码就没法测试。 Git 的核心作用:版本控制 + 多人协作,解决以上所有问题 —— 它就像代码的 “时光机”+“协作神器”,能记录每一次修改,还能让多人并行开发不冲突。 二、Git 核心概念:先把 “地基” 打牢(15 分钟)

By Ne0inhk
Git 使用技巧——查看 Commit 修改文件的概要

Git 使用技巧——查看 Commit 修改文件的概要

Git 使用技巧——查看 Commit 修改文件的概要 在日常 Git 版本管理中,经常需要查询某个 Commit 修改了哪些文件,甚至每个文件的增删行数统计,本文整理了多种实用方法,覆盖不同使用场景,满足从「简洁文件列表」到「详细行数统计」的各类需求。 一、前置准备:获取 Commit ID 所有操作都需要先获取目标 Commit 的 commit-id,commit-id 是一串 40 位的哈希值,Git 支持简写前 6-8 位使用,获取方式如下: # 简洁格式查看提交历史(优先推荐,输出包含 简写commit-id + 提交说明) git log --oneline # 完整格式查看提交历史(包含完整 commit-id、作者、时间、

By Ne0inhk