函数模版实例化

news2024/9/24 11:33:06

目录

一、前言

二、 什么是C++模板

💦泛型编程的思想 

 💦C++模板的分类

 三、函数模板

 💦函数模板概念

 💦函数模板格式

💦函数模板的原理

 💦函数模板的实例化

🍎隐式实例化

 🍉显式实例化

💦模板支持多个模板参数

 💦模板参数的匹配原则

四、类模板 

 💦类模板的概念

 💦类模板格式

 💦类模板的实例化

 💦类模板的分离编译

五、总结

六、共勉 


一、前言

        在我们学习C++时,常会用到函数重载。而函数重载,通常会需要我们编写较为重复的代码,这就显得臃肿,且效率低下。重载的函数仅仅只是类型不同,代码的复用率比较低,只要有新类型出现时,就需要增加对应的函数。此外,代码的可维护性比较低,一个出错可能会导致所有的重载均出错。

       那么,模板的出现,就让这些问题有了解决方案,所以本次博客将为大家详细的讲解C++的模板!!

二、 什么是C++模板

        程序设计中经常会用到一些程序实体它们的实现和所完成的功能基本相同,不同的仅 仅是所涉及的数据类型不同。而模板正是一种专门处理不同数据类型的机制。


       模板------是泛型程序设计的基础(泛型generic type——通用类型之意)。
 

        函数、类以及类继承为程序的代码复用提供了基本手段,还有一种代码复用途径——类属类型(泛型),利用它可以给一段代码设置一些取值为类型的参数(注意:这些参数 的值是类型,而不是某类型的数据),通过给这些参数提供一些类型来得到针对不同类 型的代码。

💦泛型编程的思想 

         首先我们来看一下下面这三个函数,如果学习过了C++函数重载 和 C++引用 的话,就可以知道下面这三个函数是可以共存的,而且传值会很方便


      
      
  1. void Swap(int & left, int & right)
  2. {
  3. int temp = left;
  4. left = right;
  5. right = temp;
  6. }
  7. void Swap(double & left, double & right)
  8. {
  9. double temp = left;
  10. left = right;
  11. right = temp;
  12. }
  13. void Swap(char & left, char & right)
  14. {
  15. char temp = left;
  16. left = right;
  17. right = temp;
  18. }

        但是真的很方便吗?这里只有三种类型的数据需要交换,若是我们需要增加交换的数据呢?再CV然后写一个函数吗?
        这肯定是不现实的,所以很明显函数重载虽然可以实现,但是有一下几个不好的地方:

  • 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数
  • 代码的可维护性比较低,一个出错可能所有的重载均出错

       

        那是否能做到这么一点,告诉编译器一个模子,让编译器根据不同的类型利用该模子来生成代码

⭐总结:
        所以,总结上面的这么一个技术,C++的祖师爷呢就想到了【模版】这个东西,告诉编译器一个模子,然后其余的工作交给它来完成,根据不同的需求生成不同的代码

这就是👉泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础

 💦C++模板的分类

1️⃣: 函数模板(function tempalte):使用泛型参数的函数(function with generic parameters)

2️⃣:类模板(class template):使用泛型参数的类(class with generic parameters)

 三、函数模板

 知晓了模版的基本概念后,首先我们要来看的就是【函数模版】

 💦函数模板概念

        函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本
        通过函数模板,可以编写一种通用的函数定义,使其能够适用于多种数据类型,从而提高代码的复用性和灵活性。

 💦函数模板格式


      
      
  1. template< typename T1, typename T2,......, typename Tn>
  2. 返回值类型 函数名(参数列表)
  3. {
  4. //……
  5. }
  6. 注意: typename是用来定义模板参数关键字,也可以使用 class(切记:不能使用 struct代替 class)

因此,交换函数就可以这样套用模板:


      
      
  1. template <typename T >
  2. void Swap(T & left, T & right)
  3. {
  4. T temp = left;
  5. left = right;
  6. right = temp;
  7. }
  8. int main()
  9. {
  10. int x = 2, y = 3;
  11. cout < < x < < " " < < y < < endl < < endl;
  12. Swap(x, y);
  13. cout < < x < < " " < < y < < endl < < endl;
  14. cout < < "-------------------------------" < < endl;
  15. char a = 'a', b = 'b';
  16. cout < < a < < " " < < b < < endl < < endl;
  17. Swap(a, b);
  18. cout < < a < < " " < < b < < endl < < endl;
  19. cout < < "-------------------------------" < < endl;
  20. double c = 2.1, d = 3.1;
  21. cout < < c < < " " < < d < < endl < < endl;
  22. Swap(c, d);
  23. cout < < c < < " " < < d < < endl < < endl;
  24. cout < < "-------------------------------" < < endl;
  25. return 0;
  26. }

 

        我们通过这个函数模版,分别传入不同数据类型的参数,通过结果的观察可以发现这个函数模版可以根据不同的类型去做一个自动推导,继而去起到一个交换的功能。

💦函数模板的原理

 💬 那我现在想问一个问题,请问它们调用的真的是同一个函数吗?
        当然不是,这里我们三次Swap不是调用同一个函数,其实Swap的时候根据不同的类型通过模板定制出专属你的类型的函数,然后再调用,这里可以通过反汇编观察到:

 

        可以发现,在进行汇编代码查看的时候,被调用的函数模版生成了两个不同的函数,它们有着不同的函数地址,因此可以回答上一小节所提出的问题了,两次所调用的函数是不一样的,是根据函数模版所生成的


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

  


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

  • 补充:

 其实库里面有一个swap函数,因此我们也不需要自己写模板了:


直接套用swap即可:


     
     
  1. int main()
  2. {
  3. int x = 2, y = 3;
  4. cout < < x < < " " < < y < < endl < < endl;
  5. swap(x, y);
  6. cout < < x < < " " < < y < < endl < < endl;
  7. cout < < "-------------------------------" < < endl;
  8. char a = 'a', b = 'b';
  9. cout < < a < < " " < < b < < endl < < endl;
  10. swap(a, b);
  11. cout < < a < < " " < < b < < endl < < endl;
  12. cout < < "-------------------------------" < < endl;
  13. double c = 2.1, d = 3.1;
  14. cout < < c < < " " < < d < < endl < < endl;
  15. swap(c, d);
  16. cout < < c < < " " < < d < < endl < < endl;
  17. cout < < "-------------------------------" < < endl;
  18. return 0;
  19. }

 💦函数模板的实例化

        用不同类型的参数使用函数模板时,称为函数模板的实例化。模板参数实例化分为:
                         1️⃣: 隐式实例化                                             2️⃣: 显式实例化

🍎隐式实例化

 隐式实例化 让编译器根据实参推演模板参数的实际类型


     
     
  1. template < class T >
  2. T Add(const T & left, const T & right)
  3. {
  4. return left + right;
  5. }
  6. int main()
  7. {
  8. int a 1 = 10, a 2 = 20;
  9. double d 1 = 10.0, d 2 = 20.0;
  10. Add(a 1, a 2); / /编译器推出T是int
  11. Add(d 1, d 2); / /编译器推出T是double
  12. return 0;
  13. }

⚠ 注意:但是我调用的时候如若这样就会出错:


     
     
  1. int main()
  2. {
  3. int a 1 = 10, a 2 = 20;
  4. double d 1 = 10.0, d 2 = 20.0;
  5. Add(a 1, d 1); / /err 编译器推不出来
  6. / *
  7. 该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型
  8. 通过实参a 1将T推演为int,通过实参d 1将T推演为double类型,但模板参数列表中只有
  9. 一个T,编译器无法确定此处到底该将T确定为int 或者 double类型而报错
  10. 注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要背黑锅
  11. */
  12. }

此时我们发现在模板函数里面,形参不匹配,就会出现报错


编译器无法确定这里的T到底是int还是double。此时有两种处理方式:

法一:用户自己来强制转化


     
     
  1. int main()
  2. {
  3. int a 1 = 10, a 2 = 20;
  4. double d 1 = 10.0, d 2 = 20.0;
  5. Add(a 1, (int)d 1); / /强制类型转换。或者 Add((double)a 1, d 1);
  6. }

法二:使用显式实例化
接下来进行讲解。

 🍉显式实例化

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

继刚才的例子,法二:使用显式实例化


     
     
  1. template < class T >
  2. T Add(const T & left, const T & right)
  3. {
  4. return left + right;
  5. }
  6. int main()
  7. {
  8. int a 1 = 10, a 2 = 20;
  9. double d 1 = 10.0, d 2 = 20.0;
  10. / /显示实例化
  11. Add <int >(a 1, d 1); / /double隐式类型转换成int
  12. Add <double >(a 1, d 2);
  13. return 0;
  14. }

💦模板支持多个模板参数


     
     
  1. template < class K, class V > / /两个模板参数
  2. void Func(const K & key, const V & value)
  3. {
  4. cout < < key < < ":" < < value < < endl;
  5. }
  6. int main()
  7. {
  8. Func( 1, 1); / /K和V均int
  9. Func( 1, 1.1); / /K是int,V是double
  10. Func <int, char >( 1, 'A'); / /多个模板参数也可指定显示实例化不同类型
  11. }


 

 💦模板参数的匹配原则

  • 原则1: 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数

     
     
  1. / /专门处理int的加法函数
  2. int Add(int left, int right)
  3. {
  4. return left + right;
  5. }
  6. / /通用加法函数
  7. template < class T >
  8. T Add(T left, T right)
  9. {
  10. return left + right;
  11. }
  12. int main()
  13. {
  14. Add( 1, 2); / /会调用哪个 Add函数?
  15. }

首先,这俩Add可以同时存在,关键是我调用Add时调的是模板函数Add,还是专门的Add?

        通过反汇编得知,调用的是专属Add函数。得出结论:编译器在调用时,有现成的就调用现成的,没有就套用模板。当然,我们也有办法强制让编译器走模板函数,如下:


     
     
  1. void Test()
  2. {
  3. Add( 1, 2); / / 与非模板函数匹配,编译器不需要特化
  4. Add <int >( 1, 2); / / 调用编译器特化的 Add版本
  5. }
  • 原则2:对于非模板函数同名函数模板如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板

     
     
  1. / / 专门处理int的加法函数
  2. int Add(int left, int right)
  3. {
  4. return left + right;
  5. }
  6. / / 通用加法函数
  7. template < class T 1, class T 2 >
  8. T 1 Add(T 1 left, T 2 right)
  9. {
  10. return left + right;
  11. }
  12. void Test()
  13. {
  14. Add( 1, 2); / / 与非函数模板类型完全匹配,不需要函数模板实例化
  15. Add( 1, 2.0); / / 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的 Add函数
  16. }

四、类模板 

 💦类模板的概念

        类模板是对成员数据类型不同的类的抽象,它说明了类的定义规则,一个类模板可以生成多种具体的类。与函数模板的定义形式类似 类模板也是使用template关键字和尖括号“<>”中的模板形参进行说明,类的定义形式与普通类相同。

 💦类模板格式

  • 首先来看到的就是其定义格式,函数模版加在函数上,那对于类模版的话就是加在类上

     
     
  1. template < class T 1, class T 2, ..., class Tn >
  2. class 类模板名
  3. {
  4. / / 类内成员定义
  5. };

我们以下面这个Stack类为例来进行讲解

  • 如果你学习了模版的相关知识后,一定会觉得这个类的限制性太大了只能初始化一个具有整型数据的栈,如果此时我想要放一些浮点型的数据进来的话也做不到,代码如下:

     
     
  1. / / C + + 正常情况下,如果需要不同类型的类(int char double 等)
  2. typedef int DataType;
  3. class Stack
  4. {
  5. public:
  6. / / 构造函数
  7. Stack(int capacity = 3) / /初始化列表
  8. :_array(new DataType[capacity]) / / 开辟一个DateType的动态数组,并进行初始化
  9. , _capacity(capacity)
  10. ,_ size( 0)
  11. {}
  12. void Push(DataType data)
  13. {
  14. / / CheckCapacity();
  15. _array[_ size] = data;
  16. _ size + +;
  17. }
  18. / / 其他方法...
  19. ~Stack()
  20. {
  21. delete[]_array;
  22. _array = nullptr;
  23. _ size = _capacity = 0;
  24. }
  25. private:
  26. DataType * _array;
  27. int _capacity;
  28. int _ size;
  29. };
  30. int main()
  31. {
  32. Stack s 1;
  33. return 0;
  34. }

💬 如果没有模版技术的话你会如何去解决这个问题呢?很简单那就是定义多个类
这是我们同学最擅长的事,CV一下两个栈就有了,StackInt存放整型数据,StackDouble存放浮点型数据


     
     
  1. class StackInt
  2. class StackDouble

但是本文我们重点要讲解的就是【模版技术】,技术界有一句话说得好 “不要重复造轮子”

  • 下面就是使用模版去定义的一个类,简称【模板类】,不限制死数据类型,将所有的DataType都改为【T】,代码如下:

     
     
  1. template < class T 1 >
  2. class Stack
  3. {
  4. public:
  5. / / 构造函数
  6. Stack(int capacity = 4)
  7. :_a(new T 1[capacity])
  8. ,_capacity(capacity)
  9. ,_ size( 0)
  10. {}
  11. void Push(T 1 data)
  12. {
  13. _a[_ size] = data;
  14. _ size + +;
  15. }
  16. / / ...其他方法
  17. / / 析构函数
  18. ~Stack()
  19. {
  20. delete[]_a;
  21. _a = nullptr;
  22. _capacity = _ size = 0;
  23. }
  24. private:
  25. T 1 * _a;
  26. int _capacity;
  27. int _ size;
  28. };
  29. int main()
  30. {
  31. Stack <int > s 1;
  32. Stack <double > s 2;
  33. return 0;
  34. }


 

 💦类模板的实例化

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

  • 可以看到因为我们将这个类定义为了类模版,此时便可以去初始化不同数据类型的栈了,上面说到过Stack是类名,但是像Stack<int>Stack<double>这些都是它的类型

     
     
  1. int main()
  2. {
  3. Stack <int > s 1; / / int
  4. Stack <double > s 2; / / double
  5. Stack <char > s 3; / / char
  6. return 0;
  7. }

 💦类模板的分离编译

        上面这样写的栈代码,其实并不是最规范的写法,还记得我们在学习C++类和对象讲到过一个类要声明和定义分离,那对于模板类也同样适用,我们马上来看看

首先进行栈 类的模板声明:


     
     
  1. #include <iostream >
  2. #include <stdio.h >
  3. #include <stdlib.h >
  4. using namespace std;
  5. template < class T 1 >
  6. class Stack
  7. {
  8. public:
  9. / / 构造函数
  10. Stack(int capacity = 4);
  11. / /插入函数
  12. void Push(T 1 data);
  13. / / 其他方法....
  14. / / 析构函数
  15. ~Stack();
  16. private:
  17. T 1 * _a;
  18. int _capacity;
  19. int _ size;
  20. };

其次进行栈 类的模板定义:先看一下构造函数的定义


     
     
  1. / / 构造函数
  2. Stack ::Stack(int capacity)
  3. :_a(new T 1[capacity])
  4. ,_capacity(capacity)
  5. ,_ size( 0)
  6. {}

  • 不过呢可以看到直接像我们之前那样去进行类外定义似乎行不通,说缺少类模版“Stack”的参数列表因为这个成员函数内部也使用到了模版参数T,那么这个函数也要变为函数模版才行
  • 这里要强调一点的是对于普通类来说类名和类型是一样的, 像构造函数,它的函数名就是类名;可是对于模板类来说是不一样,类名和类型不一样,这里Stack只是这个模版类的类名罢了,但我们现在需要的是类型,此处就想到了我们在上面所学的【显式实例化】,这个模板类的类型即为Stack<T>

所以正确的写法为:


     
     
  1. #include "Stack.h"
  2. template < class T 1 >
  3. / / 构造函数
  4. Stack <T 1 > ::Stack(int capacity)
  5. :_a(new T 1[capacity])
  6. ,_capacity(capacity)
  7. ,_ size( 0)
  8. {}
  9. template < class T 1 >
  10. / / 插入函数
  11. void Stack <T 1 > ::Push(T 1 data)
  12. {
  13. _a[_ size] = data;
  14. _ size + +;
  15. }
  16. / / 析构函数
  17. template < class T 1 >
  18. Stack <T 1 > ::~Stack()
  19. {
  20. delete[]_a;
  21. _a = nullptr;
  22. _capacity = _ size = 0;
  23. }

⚠ 注意:类模板,是不支持,声明,定义,测试分开写的,会出现链接编译错误,如下:



解决方法:将测试和定义写在一起

这样就不会报错啦!

五、总结

  •  首先呢我们介绍了什么是【函数模版】,新学习了一个关键字叫做template,用它再配合模版参数就可以去定义出一个函数模版,有了它,我们在写一些相同类型函数的时候就无需去进行重复的CV操作了,在通过汇编观察函数模版的原理后,清楚了我们只需要传入不同的类型,此时模版参数就会去进行一个自动类型推导,从而产生不同的函数。函数模版定义好后还要对其实例化才能继续使用,但此时要注意的一点是如果传递进去的类型个数与模版参数的个数不匹配的话,其就无法完成自动类型推导,因为这会产生一个歧义。所以想要真正学好模版,这点是一定要搞清楚的!!!
  • 接下去呢我们又学习了【类模版】,没想到吧,类也可以变成一个模版,以Stack类为例,对于类模版而言,其类名和类型与普通类是不一样的,这点要注意了,尤其体现在类的成员函数放在类外进行定义的时候,也要将其定义为函数模版,函数名前面指明其类型,这才不会出问题。有了类模版之后,我们去显式实例化不同的数据类型后也可以让模版参数去做一个自动类型推导从而得到不同数据类型的栈

六、共勉 

     以下就是我对C++ 模板的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新对C++STL库的理解,请持续关注我哦!!!   

文章知识点与官方知识档案匹配,可进一步学习相关知识
算法技能树首页概览 60422 人正在系统学习中

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

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

相关文章

Pytorch下张量的形状操作(详细)

目录 一、基本操作函数 二、分类&#xff1a;维度改变&#xff0c;张量变形&#xff0c;维度重排 2.1维度改变 2.2张量变形 2.3维度重排 三、实例 一、基本操作函数 在PyTorch中&#xff0c;对张量的形状进行操作是常见的需求&#xff0c;因为它允许我们重新组织、选择和…

53、图论-课程表

思路&#xff1a; 其实就是图的拓扑排序&#xff0c;我们可以构建一个图形结构&#xff0c;比如[0,1]表示1->0&#xff0c;对于0来说入度为1。 遍历结束后&#xff0c;从入度为0的开始遍历。引文只有入度为0的节点没有先决条件。然后依次减少1。直到所有节点入度都为0.然后…

Ai-WB2 系列模组SDK接入亚马逊云

文章目录 前言一、准备二、亚马逊云物模型建立1. 注册亚马逊账号&#xff0c;登录AWS IoT控制台&#xff0c;[注册地址](https://aws.amazon.com/cn/)2. 创建好之后点击登录3. 创建物品以及下载证书 三、连接亚马逊云demo获取以及配置1. 下载源码2. 按照顺序执行下面指令3. 修改…

Tensorflow AutoGraph 的作用和功能

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ TensorFlow AutoGraph 是 TensorFlow 中的一个重要特性&#xff0c;它允许开发者使用普通的 Python 语法编写高效的 TensorFlow 图&#xff08;graph&#xff09;。这意味着开发者可以利用 Python 的易…

【51单片机项目】基于51单片机自制多功能小键盘/模拟USB键盘【附源码】(STC89C52RC+CH9328)

目录 一、效果展示 二、创作灵感 三、硬件电路 注意事项 工作原理 四、源码 main.c 五、附录 CH9328工作原理 CH9328的模式选择 ​编辑 全键盘键码值表 参考链接 一、效果展示 该小键盘具有三种功能&#xff1a; 1、自动输入开机密码 2、每隔一段时间自动按下ct…

在Mac M1笔记本上跑大语言模型llama3的4个步骤?(install、pull、run、ask)

要点 Ollama一个功能强大的本地大语言模型LLM运行工具&#xff0c;支持很多模型&#xff0c;并且操作极其简单快速回忆步骤&#xff1a; 下载ollama工具&#xff1a;https://ollama.com/download 下载模型&#xff1a;ollama pull llama3 #根据libs列表直接指定名字 运行模型…

【软考】设计模式之适配器模式

目录 1. 说明2. 应用场景3. 结构图4. 构成5. 优缺点5.1 优点5.2 缺点 6. 适用性7. java示例7.1 类适配器模式7.2 对象适配器模式 1. 说明 1.Adapter&#xff08;适配器&#xff09;。2.将一个类的接口转换成客户希望的另外一个接口。3.Adapter模式使得原本由于接口不兼容而不能…

visionTransformer window平台下报错

错误&#xff1a; KeyError: Transformer/encoderblock_0/MlpBlock_3/Dense_0kernel is not a file in the archive解决方法&#xff1a; 修改这个函数即可&#xff0c;主要原因是Linux系统与window系统路径分隔符不一样导致 def load_from(self, weights, n_block):ROOT f&…

物理隔离条件下的数据安全导入导出方案,哪种最安全可控?

数据安全在当今信息化社会中扮演着至关重要的角色&#xff0c;尤其像政府、军工等单位&#xff0c;有比较多的核心数据要保护&#xff0c;一旦出现数据泄漏&#xff0c;将造成不可估量的后果。因此为了保护数据安全&#xff0c;政府、军工等单位一般会采取纯物理隔离&#xff0…

day07 51单片机-串口通信

51 单片机-串口通信 1 串口通信 1.1 需求描述 本案例讲解如何通过串口和PC以9600波特率,无校验位、1停止位通信。最终实现PC向单片机发送字符串,单片机回复PC。本案例中采用串口1通信。 1.2 硬件设计 1.2.1 串口工作原理 串口是将数据按照比特逐一发送的通信接口。在串…

视频教程下载:ChatGPT驱动的SEO、网络营销、生产力提升

用户遇到的一个常见问题是在ChatGPT对话过程中难以保持清晰的目的和专注。这可能导致互动无效和浪费时间。这门课程将教给各种创意人士——艺术家、制造者、博主、讲师和内容创作者——如何制定理想的提示配方&#xff0c;从而产生更有成效的对话和更高的回报。 这是一门关于如…

《R语言与农业数据统计分析及建模》学习——数据框的向量化操作

1、向量化操作的概念和有时 向量化操作是指对整个数据结构进行一次性操作&#xff0c;而不需要使用显式的循环结构&#xff08;即同时处理整个数据框的元素&#xff0c;而不需要使用for循环逐个处理每个元素&#xff09;。优势如下&#xff1a; 代码简洁&#xff1a;不需要编写…

3.Docker常用镜像命令和容器命令详解

文章目录 1、Docker镜像命令1.1 获取镜像1.2 查看镜像1.2.1、images命令列出镜像1.2.2、tag命令添加镜像标签1.2.3、inspect命令查看详细信息1.2.4、history命令查看镜像历史 1.3 搜索镜像1.4 删除和清理镜像1.4.1、使用标签删除镜像1.4.2、清理镜像 1.5 创建镜像1.5.1、基于已…

node.js如何实现留言板功能?

一、实现效果如下&#xff1a; 20240422_160404 二、前提配置&#xff1a; 配置&#xff1a;需要安装并且导入underscore模板引擎 安装&#xff1a;在控制台输入npm install underscore -save 文件目录配置&#xff1a; 1》在文件里建一个data文件夹&#xff0c;此文件夹下…

SLS 查询新范式:使用 SPL 对日志进行交互式探索

作者&#xff1a;无哲 引言 在构建现代数据和业务系统的过程中&#xff0c;可观测性已经变得至关重要&#xff0c;日志服务&#xff08;SLS&#xff09;为 Log/Trace/Metric 数据提供了大规模、低成本、高性能的一站式平台服务&#xff0c;并提供数据采集、加工、投递、分析、…

【PhpStorm的环境配置与应用的简单介绍】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

AWD线下攻防万字最完整战术(记第一届“长城杯”半决赛战术)

目录 准备阶段 1.登录比赛平台&#xff08;获取资产&#xff09; 查看账号账号修改 服务器SSH口令mysqlWEB服务口令(后台密码)数据库后台管理员密码 账号用户检查 2.dump源码&#xff08;方便应急响应恢复靶机&#xff09; 网站源码备份 压缩文件解压文件备份到服务器本地上传…

探索大型语言模型(LLM)在人类性格个性评估(MBTI)中的前景与应用

1.概述 大型语言模型&#xff08;LLM&#xff09;如ChatGPT在各个领域的应用确实越来越广泛&#xff0c;它们利用庞大的数据集进行训练&#xff0c;以模拟人类的语言理解和生成能力。这些模型在提供信息、解答问题、辅助决策等方面表现出了强大的能力&#xff0c;但它们并不具…

Docker pull镜像名称 把本地镜像推送到远程详解

Docker pull镜像名称 把本地镜像推送到远程详解&#xff1a; Docker 镜像 仓库 容器介绍 以及镜像仓库详解 下载一个alpine的镜像演示&#xff0c;alpine是一个比较小的的linux镜像。 docker pull alpinedocker tag d4ff818577bc docker.io/itying/alpine:v1.0.1docker tag d4…

Macs Fan Control Pro for Mac:全面优化Mac风扇控制软件

Macs Fan Control Pro for Mac是一款专为苹果电脑用户设计的风扇控制软件&#xff0c;旨在通过精确的风扇速度调节&#xff0c;全面优化Mac的散热性能&#xff0c;确保系统始终运行在最佳状态。 Macs Fan Control Pro for Mac中文版下载 该软件具备实时监控功能&#xff0c;能够…