【C++入门到精通】C++入门 —— 模版(template)

在这里插入图片描述

阅读导航

  • 前言
  • 一、模版的概念
  • 二、函数模版
    • 1. 函数模板概念
    • 2. 函数模板定义格式
    • 3. 函数模板的原理
    • 4. 函数模版的实例化
      • 🚩隐式实例化
      • 🚩显式实例化
    • 5. 函数模板的匹配原则
  • 三、类模板
    • 1. 类模板的定义格式
    • 2. 类模板的实例化
  • 四、非类型模板参数
    • 1. 概念
    • 2. 定义
  • 五、模板的特化
    • 1. 概念
    • 2. 函数模版特化
    • 3. 类模版特化
      • ⭕全特化
      • ⭕偏特化
    • 4. 模版特化应用示例
  • 六、模板分离编译
    • 1. 什么是分离编译
    • 2. 模版的分离编译
  • 七、模版的优缺点
    • 【优点】
    • 【缺点】
  • 温馨提示

前言

前面我们讲了C语言的基础知识,也了解了一些数据结构,并且讲了有关C++的命名空间的一些知识点以及关于C++的缺省参数、函数重载,引用 和 内联函数也认识了什么是类和对象以及怎么去new一个 ‘对象’ ,以及学习了几个STL的结构也相信大家都掌握的不错,接下来博主将会带领大家继续学习有关C++比较重要的知识点—— 模版(template)。下面话不多说坐稳扶好咱们要开车了😍

一、模版的概念

模板是C++中的一种编程工具,它允许使用通用代码来定义函数和类,以适应多种类型或值的需求,从而实现代码的复用和泛化。模板实质上是一种参数化的类型或值的规范。通过模板的使用,可以提高代码的复用性和拓展性,使得代码更加通用并能适应不同类型或值的需求。模板可以在编译时生成针对不同类型或值的代码,从而提高代码的效率和灵活性。在C++中,有两种类型的模板:函数模板和类模板。下面博主来逐个介绍。

二、函数模版

1. 函数模板概念

函数模板允许定义一个通用的函数其中一些或全部的参数的类型可以是参数化的。使用函数模板时,编译器根据实际使用的参数类型,自动生成对应的函数代码

2. 函数模板定义格式

template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表)
{
}

🚨注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)

使用模版定义一个交换函数

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

使用模版定义一个取较大值函数

template <typename T>
T getMax(T a, T b) {return a > b ? a : b;
}int main() {int maxInt = getMax(2, 5); // 使用函数模板实例化为 int 类型的函数double maxDouble = getMax(3.14, 2.5); // 使用函数模板实例化为 double 类型的函数// ...
}

3. 函数模板的原理

函数模板是一个蓝图,它本身并不是函数是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器。

在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此。

4. 函数模版的实例化

用不同类型的参数使用函数模板时,称为函数模板的实例化。函数模板的实例化过程可以分为两个步骤:模板参数推断和模板函数生成。

  1. 模板参数推断:编译器根据实际传入的参数类型推导出模板参数的具体类型。编译器会尝试根据传递的参数类型来匹配模板参数,并确定参数的具体类型。如果无法进行准确的匹配,则可能会产生模板参数推断失败的错误。

  2. 模板函数生成:根据推断出的模板参数类型,编译器生成特定类型的函数代码。编译器使用推断出的参数类型来替换函数模板中的模板参数,生成与传递的参数类型匹配的函数定义。

函数模板的实例化是在编译时完成的,它提前为不同的参数类型生成了不同的函数定义,以提高代码的重用性和执行效率。

⭕模板参数实例化分为:隐式实例化和显式实例化

🚩隐式实例化

下面是一个示例,展示函数模板隐式实例化的过程:

template <typename T>
T getMax(T a, T b) {return a > b ? a : b;
}int main() {int maxInt = getMax(2, 5); // 实例化为 int 类型的函数,参数推断为 intdouble maxDouble = getMax(3.14, 2.5); // 实例化为 double 类型的函数,参数推断为 doublechar maxChar = getMax('a', 'b'); // 实例化为 char 类型的函数,参数推断为 char// ...
}

在这个例子中,编译器会根据传递的参数类型自动推断模板参数的类型,并生成对应类型的函数代码。实例化后会生成 getMax 函数的具体定义,其中的模板参数 T 被替换为相应的类型。

🚩显式实例化

模板的显式实例化是指在编译时明确告诉编译器需要实例化的模板类型,以生成对应的函数定义

在函数名后的<>中指定模板参数的实际类型

下面是一个示例,展示函数模板显式实例化的过程:

template <typename T>
T getMax(T a, T b) {return a > b ? a : b;
}
int main() {int maxInt = getMax<int>(2, 5); // 显式实例化的 int 类型的函数int maxDouble = getMax<double>(1.1, 5.2)// 显式实例化的 double类型的函数// ...
}

通过显式实例化,可以在编译时生成特定类型的函数定义,避免了模板参数推断和函数生成的开销,提高了代码的执行效率

5. 函数模板的匹配原则

  1. 最佳匹配原则:编译器会尝试找到与调用参数最匹配的函数模板来实例化。在函数模板的候选函数中,编译器会根据实际参数类型进行以下规则的匹配:

    a. 完全匹配:如果有一个函数模板能够完全匹配实际参数的类型,那么它将被选择为最佳匹配。

    b. 类型转换匹配:如果有多个函数模板能够通过一系列的类型转换(如隐式类型转换)匹配实际参数的类型,那么转换次数最少的模板将被选择为最佳匹配。

    c. 模板特化匹配:如果存在与调用参数类型完全匹配的模板特化,那么它将被选择为最佳匹配。

    d. 不匹配:如果没有找到合适的模板来匹配调用参数的类型,那么将导致编译错误。

  2. 函数模板的特例化规则:当函数模板的特化版本和常规模板同时存在时,编译器会优先选择特化版本

下面的代码展示了函数模板匹配原则的应用:

template <typename T>
void print(T value) {std::cout << value << std::endl;
}template <>
void print(int value) {std::cout << "Specialized: " << value << std::endl;
}void print(double value) {std::cout << "Non-template: " << value << std::endl;
}int main() {print(5); // 调用特化版本的 print,输出 "Specialized: 5"print(3.14); // 调用非模板函数 print,输出 "Non-template: 3.14"print("Hello"); // 调用普通模板函数 print,输出 "Hello"return 0;
}

在上述示例中,当调用 print 函数时,根据参数类型的不同,编译器将根据匹配原则选择最佳匹配的函数版本。如果有特化版本,将优先选择特化版本。如果没有特化版本,会选择模板函数中最适合的版本来实例化

三、类模板

1. 类模板的定义格式

定义多个类型

template<class T1, class T2, ..., class Tn>
class 类模板名
{// 类内成员定义
};

定义单个类型

template <typename T>
class ClassName {// 类模板的成员和方法声明及定义
};

在上述格式中,template <typename T> 表示定义了一个类模板,T 是一个类型参数,它可以在类的成员和方法中使用。你可以根据需要使用其他的类型参数名称。

下面的代码展示了一个简单的类模板的定义:

🚨注意:Pair 不是具体的类,是编译器根据被实例化的类型生成具体类的模具template <typename T>
class Pair {
private:T first;T second;public:Pair(T f, T s) : first(f), second(s) {}T getFirst() const {return first;}T getSecond() const {return second;}void setFirst(T f) {first = f;}void setSecond(T s) {second = s;}
};

Pair 是一个类模板,拥有两个泛型成员变量 firstsecond,以及一些泛型成员函数。通过类模板,我们可以定义一个通用的配对(Pair)类,用于存储任意类型的一对值

2. 类模板的实例化

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

例如:

int main() {Pair<int> p(3, 4);  // 实例化一个 Pair 类型对象,其中 T 被替换为 intPair<double> q(1.5, 2.7);  // 实例化一个 Pair 类型对象,其中 T 被替换为 doubleint first = p.getFirst();  // 获取 p 对象中的第一个值 3double second = q.getSecond();  // 获取 q 对象中的第二个值 2.7p.setSecond(7);  // 设置 p 对象的第二个值为 7return 0;
}

在上面的代码中,我们使用不同的具体类型参数实例化了 Pair 类模板,并使用相应的对象进行操作。编译器会根据实际传递的类型参数替换类模板中的类型参数 T,生成对应的类定义和对象实例化

四、非类型模板参数

1. 概念

非类型模板参数是指在C++中,模板参数可以不仅仅是类型,还可以是常量表达式。非类型模板参数允许在模板实例化时传递常量值作为参数,并在编译时对其进行计算和使用。

⭕通过使用非类型模板参数,可以实现在编译时生成特定类型或值的代码

🚨非类型模板参数必须是以下几种类型之一

  1. 整数类型,包括整数、字符和枚举类型。
  2. 指针类型。
  3. 引用类型。

浮点数、类对象和字符串是不允许作为非类型模板参数的
因为非类型模板参数在编译时需要被计算和处理,而浮点数、类对象和字符串类型的计算和处理是在运行时进行的,无法在编译时确定。

2. 定义

在定义模板时,可以使用非类型模板参数来指定一个或多个参数。例如:

template <typename T, int SIZE>
class Array {T data[SIZE];// ...
};

在这个例子中,模板参数SIZE是一个非类型的整数参数,用于指定数组的大小。在实例化Array模板时,需要指定一个整数常量作为SIZE的值。

五、模板的特化

1. 概念

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理。比如:实现了一个专门用来进行小于比较的函数模板。

// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{return left < right;
}int main()
{cout << Less(1, 2) << endl; // 可以比较,结果正确Date d1(2022, 7, 7);Date d2(2022, 7, 8);cout << Less(d1, d2) << endl; // 可以比较,结果正确Date* p1 = &d1;Date* p2 = &d2;cout << Less(p1, p2) << endl; // 可以比较,结果错误return 0;
}

可以看到,Less绝对多数情况下都可以正常比较,但是在特殊场景下就得到错误的结果。上述示例中,p1指向的d1显然小于p2指向的d2对象,但是Less内部并没有比较p1p2指向的对象内容,而比较的是p1p2指针的地址,这就无法达到预期而错误。此时,就需要对模板进行特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化

2. 函数模版特化

函数模板的特化步骤:

  1. 必须要先有一个基础的函数模板
  2. 关键字template后面接一对空的尖括号<>
  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
  4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{return left < right;
}// 对Less函数模板进行特化
template<>
bool Less<Date*>(Date* left, Date* right)
{return *left < *right;
}
int main()
{cout << Less(1, 2) << endl;Date d1(2022, 7, 7);Date d2(2022, 7, 8);cout << Less(d1, d2) << endl;Date* p1 = &d1;Date* p2 = &d2;cout << Less(p1, p2) << endl; // 调用特化之后的版本,而不走模板生成了
return 0;
}

3. 类模版特化

⭕全特化

完全特化是指对于特定的类型或参数,提供了一个完全定制的模板实现。在完全特化中,给定的特化版本针对特定类型或参数提供了特定的实现,这个特化版本将完全替代泛化模板。可以通过显式声明来完成完全特化,使用 template<> 来指示特化版本。

下面是一个完全特化的示例:

template <typename T>
struct MyClass {void doSomething() {// 泛化版本的实现}
};template <>
struct MyClass<int> {void doSomething() {// 针对 int 类型的完全特化实现}
};

在上面的示例中,我们定义了一个模板类 MyClass,并对其进行了完全特化。在 MyClass<int> 的特化版本中,我们针对 int 类型提供了一个特定的成员函数实现。

⭕偏特化

偏特化是指对部分类型或参数进行特化,针对特定的形式或范围进行自定义处理。偏特化可以有多个参数,并对其中一个或多个参数进行特化。相对于完全特化,偏特化可以提供更灵活的定制需求。

下面是一个偏特化的示例:

template <typename T, typename U>
struct MyClass {void doSomething() {// 泛化版本的实现}
};template <typename T>
struct MyClass<T, int> {void doSomething() {// 对于第二个参数为 int 的偏特化实现}
};

在上面的示例中,我们定义了一个模板类 MyClass,并对其进行了偏特化。在 MyClass<T, int> 的特化版本中,我们针对第二个参数为 int 的情况提供了一个特定的成员函数实现。

🚨注意,特化版本的成员函数可以是不同的,甚至可以有不同的成员变量和特定的行为

4. 模版特化应用示例

下面是一个使用函数模板特化的示例,展示了如何实现针对特定类型的特定行为:

#include <iostream>// 泛化版本的模板函数
template <typename T>
void showType(T value) {std::cout << "Value: " << value << " is of unknown type\n";
}// 特化版本的模板函数,针对字符串类型
template <>
void showType<std::string>(std::string value) {std::cout << "Value: " << value << " is a string\n";
}// 特化版本的模板函数,针对整型类型
template <>
void showType<int>(int value) {std::cout << "Value: " << value << " is an integer\n";
}int main() {showType("Hello");  // 使用特化版本的模板函数,输出 "Value: Hello is a string"showType(123);     // 使用特化版本的模板函数,输出 "Value: 123 is an integer"showType(3.14);    // 使用泛化版本的模板函数,输出 "Value: 3.14 is of unknown type"return 0;
}

在上述示例中,我们定义了一个模板函数 showType,用于根据传入的参数类型显示该值的类型信息。

通过模板特化,我们为特定类型(std::stringint)提供了特定的实现方式。在主函数中,我们分别调用了 showType 函数并传入不同的参数类型,从而分别调用了泛化版本和特化版本的模板函数。

这样我们可以根据不同的类型提供特定的处理方式,以满足特定需求。运行结果为:

Value: Hello is a string
Value: 123 is an integer
Value: 3.14 is of unknown type

六、模板分离编译

1. 什么是分离编译

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。

2. 模版的分离编译

模板的分离编译可以将模板的声明和实现分离到不同的文件,这样每个源文件只需要编译一次模板的实现,减少了代码冗余和编译时间。

🚨🚨注意:每次进行编译都要进行模版实例化,如果不实例化或者参数与实例化的不匹配,编译器在进行链接时会报错,所以不建议进行分离编译

下面是一个使用模板的分离编译的示例:

  1. 头文件 mytemplate.h 包含了模板的声明:
#ifndef MYTEMPLATE_H
#define MYTEMPLATE_Htemplate <typename T>
class MyClass {
public:void print(T value);
};#endif
  1. 源文件 mytemplate.cpp 包含了模板的实现:
#include <iostream>
#include "mytemplate.h"template <typename T>
void MyClass<T>::print(T value) {std::cout << "Value: " << value << std::endl;
}// 显式实例化模板,以确保编译器生成该类型的代码
template class MyClass<int>;
  1. 主函数所在的源文件 main.cpp 使用了模板,但没有包含实现:
#include "mytemplate.h"int main() {MyClass<int> obj;obj.print(42);return 0;
}

在上述示例中,mytemplate.h 包含了模板的声明,mytemplate.cpp 包含了模板的实现,并且通过使用 template class MyClass<int> 显式实例化了模板的 int 特化版本(必须要实例化,否则就会报错)

七、模版的优缺点

【优点】

  1. 通用性:模板提供了一种通用的编程方式,可以在不同的类型上进行操作和实例化,增强了代码的复用性和可扩展性。

  2. 静态类型检查:模板在编译时进行类型检查,可以捕获一些类型错误和逻辑错误,提前发现问题并减少运行时错误。

  3. 高性能:模板生成的代码在编译时会生成特定类型的实现,避免了运行时的类型转换和动态分派,提供了更高的执行效率。

  4. 泛化算法:模板可以用于实现各种泛化算法,无需为不同的数据类型编写不同的代码,减少了重复劳动和代码维护成本。

【缺点】

  1. 长编译时间:模板通常在编译时进行实例化和展开,对于复杂的模板和大规模的代码库,编译时间可能会显著增加。

  2. 可读性差:模板的代码通常比非模板代码更复杂,对于初学者或不熟悉模板编程的人来说,理解和维护模板代码可能更加困难。

  3. 编译错误信息难以理解:当模板出现编译错误时,编译器生成的错误消息可能很难理解和定位,给调试带来一定的困难。

  4. 扩展性受限:对于已实例化的模板,无法在运行时动态地添加新的类型支持,如果需要支持新的类型或功能,需要重新编译模板。

温馨提示

感谢您对博主文章的关注与支持!另外,我计划在未来的更新中持续探讨与本文相关的内容,会为您带来更多关于C++以及编程技术问题的深入解析、应用案例和趣味玩法等。请继续关注博主的更新,不要错过任何精彩内容!

再次感谢您的支持和关注。期待与您建立更紧密的互动,共同探索C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

攻击LNMP架构Web应用

环境配置(centos7) 1.php56 php56-fpm //配置epel yum install epel-release rpm -ivh http://rpms.famillecollet.com/enterprise/remi-release-7.rpm//安装php56&#xff0c;php56-fpm及其依赖 yum --enablereporemi install php56-php yum --enablereporemi install php…

vscode里配置C#环境并运行.cs文件

vscode是一款跨平台、轻量级、开源的IDE, 支持C、C、Java、C#、R、Python、Go、Nodejs等多种语言的开发和调试。下面介绍在vscode里配置C#环境。这里以配置.Net SDK v5.0&#xff0c;语言版本为C#9.0&#xff0c;对应的开发平台为VS2019&#xff0c;作为案例说明。 1、下载vsc…

【LeetCode-经典面试150题-day9]

目录 36.有效的数独 54.螺旋矩阵 48.旋转图像 73.矩阵置零 36.有效的数独 题意&#xff1a; 请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 &#xff0c;验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1…

25岁无经验入行软件测试的感悟,写给还在迷茫中的你

转行软件测试两年了&#xff0c;这两年来&#xff0c;从刚开始对测试认识的朦朦胧胧&#xff0c;现在思路也逐渐清晰了&#xff0c;也明确了自己的发展方向。虽然对那些测试理论和测试工具以及测试技术有了一些加强&#xff0c;但是自我感觉还是不够深入。 我一直希望能真正融…

《网络是怎样连接的》(四)

本文主要取材于 《网络是怎样连接的》 第四章。 目录 4.1 互联网的基本结构 4.2光纤接入网&#xff08;FTTH&#xff09; 4.3 接入网中使用的PPP和隧道 4.4 网络运营商的内部 4.5 跨越运营商的网络包 简述&#xff1a;本文主要内容是解释 网络包是如何通过互联网接入路由…

【0823作业】C++:实现类嵌套,以及其构造函数、析构函数和拷贝构造函数

要求&#xff1a; 设计一个Per类。类中包含私有成员&#xff1a;姓名、年龄、指针成员身高、体重&#xff1b; 再设计一个Stu类&#xff0c;类中包含私有成员&#xff1a;成绩、Per类对象 p1&#xff1b; 设计这两个类的构造函数、析构函数和拷贝构造函数。 #include <iostr…

【广州华锐视点】AR配电所巡检系统:可视化巡检利器

随着科技的发展&#xff0c;人工智能、大数据等技术逐渐应用于各个领域&#xff0c;为人们的生活带来便利。在电力行业&#xff0c;AR(增强现实)技术的应用也日益广泛。AR配电所巡检系统作为一种新型的巡检方式&#xff0c;可以实现多种功能&#xff0c;提高巡检效率&#xff0…

如何在Windows、Mac和Linux操作系统上安装Protocol Buffers(protobuf)编译器

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

Android类加载机制

要说Android的类加载机制 &#xff0c;就离不开 类加载器ClassLoader&#xff0c;它是一个抽象接口 下面这个图还是比较好表达了类加载流程&#xff0c;但如果不看我红色画的线&#xff0c;就会感觉有点乱&#xff0c;需要注意是采用的是双亲委派模式&#xff0c;class加载要先…

测试驱动开发(TDD)

测试驱动开发&#xff08;TDD&#xff09; 本篇文章简单叙述一下什么是测试驱动开发&#xff0c;以及怎么进行测试驱动开发&#xff01; TDD &#xff08;Test Driven Development&#xff09;&#xff1a;&#xff08;源于极限编程&#xff08;XP&#xff09;&#xff09;在不…

9.Sentinel哨兵

1.Sentinel Sentinel&#xff08;哨兵&#xff09;是由阿里开源的一款流量控制和熔断降级框架&#xff0c;用于保护分布式系统中的应用免受流量涌入、超载和故障的影响。它可以作为微服务架构中的一部分&#xff0c;用于保护服务不被异常流量冲垮&#xff0c;从而提高系统的稳定…

JavaSE 数组

定义&#xff1a; int []arr; int arr[]; 初始化 // 完整格式 int arr[] new int[]{1, 2, 3}; // 简单格式 int arr[] {1, 2, 3}; 数组的元素访问、遍历 按照下标访问即可。数组的长度函数为 arr.length()。idea快速生成遍历的方法&#xff1a;数组名.fori 静态初始化 &a…

element时间选择器el-date-picter使用disabledDate指定禁用的日期

需要的效果 <el-date-pickerclass"selectstyle"v-model"year"value-format"yyyy"type"year":picker-options"disabledCli"placeholder"选择年"> </el-date-picker>data() {return {disabledCli: {/…

5G+AI数字化智能工厂建设解决方案PPT

导读&#xff1a;原文《5GAI数字化智能工厂建设解决方案》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。数字化智能工厂定义 智能基础架构协同框架 - 端、边、云、网…

微信小程序手机号验证开发遇到问题

公司小程序项目中快速登录需要实现微信用户授权手机登录、注册功能。结果遇到了 invalid code hint: [zHkDmt0sf-MBjga] rid: 64e3259f-1091b953-7e10f1da 目录 服务端文档 文档描述 返回信息 服务端代码 遇到问题 排查问题 1.服务端用错了appid serect 2.小程序端用错…

C++(Qt)软件调试---gdb调试入门用法(12)

gdb调试—入门用法&#xff08;1&#xff09; 文章目录 gdb调试---入门用法&#xff08;1&#xff09;1、前言1.1 什么是GDB1.2 为什么要学习GDB1.3 主要内容1.4 GDB资料 2、C/C开发调试环境准备3、gdb启动调试1.1 启动调试并传入参数1.2 附加到进程1.3 过程执行1.4 退出调试 4…

Spark第二课RDD的详解

1.前言 RDD JAVA中的IO 1.小知识点穿插 1. 装饰者设计模式 装饰者设计模式:本身功能不变,扩展功能. 举例&#xff1a; 数据流的读取 一层一层的包装&#xff0c;进而将功能进行进一步的扩展 2.sleep和wait的区别 本质区别是字体不一样,sleep斜体,wait正常 斜体是静态方法…

vue3+elementPlus table里添加输入框并提交校验

<template><div><el-form :model"info" ref"forms"><el-tableref"tableRef":data"info.data"border><el-table-column align"center" property"name" label"*姓名"><…

springboot整合rabbitmq

添加依赖 <!--RabbitMQ 依赖--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId></dependency><dependency><groupId>org.springframework.boot</gro…

DFX概述 | Design For X | Design For Excellent

Design for X (DFX) Methods 什么是“Design for X”&#xff1f; Design for eXcellence是一种在设计和制造领域中的不断发展的原则哲学。它采用了全面和系统的设计方法&#xff0c;关注产品的各个方面——从概念生成到最终交付。 它提供了良好的实践和设计指南&#xff0c…