当前位置: 首页 > news >正文

C++模板笔记

Cpp模板笔记

文章目录

  • Cpp模板笔记
    • 1. 为什么要定义模板
    • 2. 模板的定义
      • 2.1 函数模板
        • 2.1.1 函数模板的重载
        • 2.1.2 头文件与实现文件形式(重要)
        • 2.1.3 模板的特化
        • 2.1.4 模板的参数类型
        • 2.1.5 成员函数模板
        • 2.1.6 使用模板的规则
      • 2.2 类模板
      • 2.3 可变参数模板

模板是一种通用的描述机制,使用模板允许使用通用类型来定义函数或类。在使用时,通用类型可被具体的类型,如 int、double 甚至是用户自定义的类型来代替。模板引入一种全新的编程思维方式,称为“泛型编程”或“通用编程”。

1. 为什么要定义模板

像C/C++/Java等语言,是编译型语言,先编译后运行。它们都有一个强大的类型系统,也被称为强类型语言,希望在程序执行之前,尽可能地发现错误,防止错误被延迟到运行时。所以会对语言本身的使用造成一些限制,称之为静态语言

与之对应的,还有动态语言,也就是解释型语言。如javascript/python/Go,在使用的过程中,一个变量可以表达多种类型,也称为弱类型语言。因为没有编译的过程,所以相对更难以调试。

强类型程序设计中,参与运算的所有对象的类型在编译时即确定下来,并且编译程序将进行严格的类型检查。为了解决强类型的严格性和灵活性的冲突,也就是在严格的语法要求下尽可能提高灵活性,有以下方式:

例如,想要实现能够处理各种类型参数的加法函数

以前我们需要进行函数重载(函数名相同,函数参数不同)

int add(int x, int y)
{return x + y;
}double add(double x, double y)
{return x + y;
}long add(long x, long y)
{return x + y;
}string add(string x, string y)
{return x + y;
}

在使用时看起来只需要调用add函数,传入不同类型的参数就可以进行相应的计算了,很方便。

但是程序员为了这种方便,实际上要定义很多个函数来处理各种情况的参数。

模板(将数据类型作为参数)

上面的问题用函数模板的方式就可以轻松解决:

//希望将类型参数化
//使用class关键字或typename关键字都可以
template <class T>
T add(T x, T y)
{return x + y;
}int main(void){cout << add(1,2) << endl;cout << add(1.2,3.4) << endl;return 0;
}

函数模板的优点:

不需要程序员定义出大量的函数,在调用时实例化出对应的模板函数,更“智能”

2. 模板的定义

模板作为实现代码重用机制的一种工具,它可以实现类型参数化,也就是把类型定义为参数,从而实现了真正的代码可重用性。

模板可以分为两类,一个是函数模版,另外一个是类模板。通过参数实例化定义出具体的函数或类,称为模板函数模板类。模板的形式如下:

// 形式
template <typename/class T1, typename T2,...>	// T1,T2称为类型参数或者模板参数

模板参数是一个更大的概念,包含了类型参数和非类型参数,这里的T1/T2属于类型参数,代表了类型。

模板发生的时机是在编译时

模板本质上就是一个代码生成器,它的作用就是让编译器根据实际调用来生成代码。

编译器去处理时,实际上由函数模板生成了多个模板函数,或者由类模板生成了多个模板类

#include <iostream>
using std::cout;
using std::endl;template <class T>
T add(T x, T y) {return x + y;
}// 模板本质上就是一个代码生成器
// 编译器处理时,实际上由函数模板生成了多个模板函数
// 或者多个模板类
#if 0
// 编译器生成的,而非显式定义的
int add(int x, int y) {return x + y;
}double add(double x, double y) {return x + y;
}
#endifint main()
{cout << add(1, 2) << endl;cout << add(1.2, 1.3) << endl;return 0;
}

2.1 函数模板

由函数模板到模板函数的过程称之为实例化

函数模板 --》 生成相应的模板函数 --》编译 —》链接 --》可执行文件

以下程序实际上可以理解为生成了四个模板函数

template <class T>
T add(T t1,T t2)
{ return t1 + t2; }void test0(){short s1 = 1, s2 = 2;int i1 = 3, i2 = 4;long l1 = 5, l2 = 6;double d1 = 1.1, d2 = 2.2;cout << "add(s1,s2): " << add(s1,s2) << endl;cout << "add(i1,i2): " << add(i1,i2) << endl;cout << "add(l1,l2): " << add(l1,l2) << endl;cout << "add(d1,d2): " << add(d1,d2) << endl;  
}

上述代码中在进行模板实例化时,并没有指明任何类型,函数模板在生成模板函数时通过传入的参数类型确定出(推导出)模板类型,这种做法称为隐式实例化

我们在使用函数模板时还可以在函数名之后直接写上模板的类型参数列表,指定类型,这种用法称为显式实例化

template <class T>
T add(T t1,T t2)
{ return t1 + t2; }void test0(){int i1 = 3, i2 = 4;cout << "add(i1,i2): " << add<int>(i1,i2) << endl;  //显式实例化
}
2.1.1 函数模板的重载

函数模板的重载分为:

(1)函数模板与函数模板重载**(谨慎使用)**

(2)函数模板与普通函数重载

函数模板与函数模板重载

如果一个函数模板无法实例化出合适的模板函数(去进行显式实例化也有一些问题)的时候,可以再给出另一个函数模板

//函数模板与函数模板重载
//模板参数个数不同,ok
template <class T> //模板一
T add(T t1,T t2)
{ 
return t1 + t2;
}template <class T1, class T2>  //模板二
T1 add(T1 t1, T2 t2)
{
return t1 + t2;
}double x = 9.1;
int y = 10;
cout << add(x,y) << endl;   //会调用模板二生成的模板函数,不会损失精度//猜测一下,这种调用方式返回的结果是什么呢?
cout << add(y,x) << endl; 

上面输出的结果会是19,调用的仍然是模板二,但返回类型和函数的第一个参数类型相同,采用隐式实例化时根据参数推导类型,推导出返回类型应该是int型。

#include <iostream>
using std::cout;
using std::endl;template <class T>
T add(T t1, T t2) {cout << "模板一" << endl;return t1 + t2;
}template <class T1, class T2>
T1 add(T1 t1, T2 t2) {cout << "模板二" << endl;return t1 + t2;
}int main()
{   int x = 8;double y = 9.8;int z = 10;cout << add(x, y) << endl;  // 17 调用模板二cout << add(y, x) << endl;  // 17.8 调用模板二cout << add<int>(y, x) << endl;  // 17 调用模板一cout << add(x, z) << endl;  // 18 调用模板一return 0;
}

事实上,在一个源文件中定义多个通用模板的写法应该谨慎使用(尽量避免)如果实在需要也尽量使用隐式实例化的方式进行调用,编译器会选择参数类型最匹配的模板(通常是参数类型需要更少转换的模板)。

函数模板与函数模板重载:

(1)首先,名称必须相同(显然)

(2)模板参数列表中的模板参数在函数中所处位置不同 —— 但不建议进行这样的重载

(3)模板参数的个数不一样时,可以构成重载(相对常见)

函数模板与普通函数重载

普通函数优先于函数模板执行——因为普通函数更快

编译器扫描到函数模板的实现时并没有生成函数,只有扫描到下面调用add函数的语句时,给add传参,知道了参数的类型,这才生成一个相应类型的模板函数——模板参数推导。所以使用函数模板一定会增加编译的时间。

此处,就直接调用了普通函数,而不去采用函数模板,因为更直接,效率更高。

//函数模板与普通函数重载
template <class T1, class T2>
T1 add(T1 t1, T2 t2)
{
return t1 + t2;
}short add(short s1, short s2){
cout << "add(short,short)" << endl;
return s1 + s2;
}void test1(){
short s1 = 1, s2 = 2;
cout << add(s1,s2) << endl;   //调用普通函数
}

如果没有普通函数,就会调用上面的函数模板,实例化出相应的模板函数。尽管s1/s2的类型相同,也是可以使用该模板的。

—— T1/T2并不一定非得是不同类型,能推导出即可。

当然,如果采用显示实例化的方式调用,肯定是调用函数模板。

2.1.2 头文件与实现文件形式(重要)

为什么C++标准头文件没有所谓的.h后缀?

在一个源文件中,函数模板的声明与定义分离是可以的,即使把函数模板的实现放在调用之下也是ok的,与普通函数一致。

//函数模板的声明
template <class T>
T add(T t1, T t2)void test1(){ int i1 = 1, i2 = 2;cout << add(i1,i2) << endl;
}//函数模板的实现
template <class T>
T add(T t1, T t2)
{return t1 + t2;
}

如果在不同文件中进行分离

如果像普通函数一样去写出了头文件、实现文件、测试文件,编译时会出现未定义报错

//add.h
template <class T>
T add(T t1, T t2);//add.cc
#include "add.h"
template <class T>
T add(T t1, T t2)
{
return t1 + t2;
}//testAdd.cc
#include "add.h"
void test0(){
int i1 = 1, i2 = 2;
cout << add(i1,i2) << endl;
}
  • 单独编译“实现文件”add.cc,使之生成目标文件,查看目标文件,会发现没有生成与add名称相关的函数。
  • 单独编译测试文件testAdd.cc,发现有与add名称相关的函数,但是没有地址,这就表示只有声明。

看起来和普通函数的情况有些不一样。

从原理上进行分析,函数模板定义好之后并不会直接产生一个具体的模板函数,只有在调用时才会实例化出具体的模板函数。

解决方法 —— 在”实现文件“中要进行调用,因为有了调用才有推导,才能由函数模板生成需要的函数

//add.cc
template <class T>
T add(T t1, T t2)
{
return t1 + t2;
}//在这个文件中如果只是写出了函数模板的实现
//并没有调用的话,就不会实例化出模板函数
void test1(){ 
cout << add(1,2) << endl;
}

但是在“实现文件”中对函数模板进行了调用,这种做法不优雅 。

设想:如果在测试文件调用时,在推导的过程中,看到的是完整的模板的代码,那么应该可以解决问题

//add.h
template <class T>
T add(T t1, T t2);#include "add.cc"

可以在头文件中加上#include “add.cc”,即使实现文件中没有调用函数模板,单独编译testAdd.cc,也可以发现问题已经解决。

因为本质上相当于把函数模板的定义写到了头文件中。

总结:

对模板的使用,必须要拿到模板的全部实现,如果只有一部分,那么推导也只能推导出一部分,无法满足需求。

换句话说,就是模板的使用过程中,其实没有了头文件和实现文件的区别,在头文件中也需要获取模板的完整代码,不能只有一部分。

C++的标准库都是由模板开发的,所以经过标准委员会的商讨,将这些头文件取消了后缀名,与C的头文件形成了区分;这些实现文件的后缀名设为了tcc

2.1.3 模板的特化

在函数模板的使用中,有时候会有一些通用模板处理不了的情况,我们可以定义普通函数或特化模板来解决。虽然普通函数的优先级更高,但有些场景下是必须使用特化模板的。它的形式如下:

  1. template后直接跟 <> ,里面不写类型
  2. 在函数名后跟 <> ,其中写出要特化的类型

比如,add函数模板在处理C风格字符串相加时遇到问题,如果只是简单地让两个C风格字符串进行+操作,会报错。

可以利用特化模板解决:

#include <iostream>
#include <string.h>
using std::cout;
using std::endl;template <class T>
T add(T t1, T t2) {cout << "通用模板" << endl;return t1 + t2;
}// 模板的特化 通用模板处理不了的情况
template <>
const char* add<const char*>(const char* p1, const char* p2) {char* pret = new char[strlen(p1) + strlen(p2) + 1]();strcpy(pret, p1);strcat(pret, p2);cout << "string特化模板" << endl;return pret;
}int main()
{   short x = 10;short y = 20;// 普通函数优于函数模板执行 因为普通函数更快cout << add(x, y) << endl;cout << add(1.1, 2.2) << endl;const char* p = add("hello", "world");cout << p << endl;delete [] p;return 0;
}

输出结果:

通用模板
30
通用模板
3.3
string特化模板
helloworld

使用模板特化时,必须要先有基础的函数模板

如果没有模板的通用形式,无法定义模板的特化形式。因为特化模板就是为了解决通用模板无法处理的特殊类型的操作。

特化版本的函数名、参数列表要和原基础的函数模板相同,避免不必要的错误。

2.1.4 模板的参数类型
  1. 类型参数

    之前的T/T1/T2等等称为模板参数,也称为类型参数,类型参数T可以写成任何类型

  2. 非类型参数

    需要是整型数据, char/short/int/long/size_t等

    不能是浮点型,float/double不可以

定义模板时,在模板参数列表中除了类型参数还可以加入非类型参数。

此时,调用模板时需要传入非类型参数的值

template <class T,int kBase>
T multiply(T x, T y){return x * y * kBase;
}void test0(){
int i1 = 3,i2 = 4;
//此时想要进行隐式实例化就不允许了,因为kBase无法推导
cout << multiply(i1,i2) << endl;  //error
cout << multiply<int,10>(i1,i2) << endl;   //ok
}

可以给非类型参数赋默认值,有了默认值后调用模板时就可以不用传入这个非类型参数的值

template <class T,int kBase = 10>
T multiply(T x, T y){return x * y * kBase;
}void test0(){int i1 = 3,i2 = 4;cout << multiply<int,10>(i1,i2) << endl;cout << multiply<int>(i1,i2) << endl;cout << multiply(i1,i2) << endl;
}

函数模板的模板参数赋默认值与普通函数相似,从右到左,右边的非类型参数赋了默认值,左边的类型参数也可以赋默认值

template <class T = int,int kBase = 10>
T multiply(T x, T y){
return x * y * kBase;
}void test0(){double d1 = 1.2, d2 = 1.2;cout << multiply<int>(d1,d2) << endl;    //okcout << multiply(d1,d2) << endl;        //ok
}

第一次的调用T代表了int,这个很好理解,因为使用模板时指定了类型参数。那么第二次也会代表int吗?

—— 结果发现返回的结果是double型的。

我们可以得出结论

优先级:指定的类型 > 推导出的类型 > 类型的默认参数

总结:在没有指定类型时,模板参数的默认值(不管是类型参数还是非类型参数)只有在没有足够的信息用于推导时起作用。当存在足够的信息时,编译器会按照实际参数的类型去调用,不会受到默认值的影响。

2.1.5 成员函数模板

上面我们认识了普通的函数模板,实际上,在一个普通类中也可以定义成员函数模板,如下:

#include <iostream>
using std::cout;
using std::endl;class Point{
public:Point(double x, double y): _ix(x), _iy(y){}// 成员函数模板不能加上virtual修饰template <class T>T convert() {return (T)_ix + (T)_iy;}
private:double _ix;double _iy;
};int main()
{Point pp(1.4, 2.3);cout << pp.convert<int>() << endl;return 0;
}

在convert函数模板中可以访问Point的数据成员,说明成员函数模板的使用原理同普通函数模板一样,在调用时会实例化出一个模板成员函数。普通的成员函数会有隐含的this指针作为参数,这里生成的模板成员函数中也会有。如果定义一个static的成员函数模板,那么在其中就不能访问非静态数据成员。

但是要注意:成员函数模板不能加上virtual修饰,否则编译器报错。

因为函数模板是在编译时生成函数,而虚函数机制起作用的时机是在运行时。

—— 如果要将成员函数模板在类之外进行实现,需要注意带上模板的声明

class Point
{
public:Point(double x,double y): _x(x), _y(y){}template <class T>T add(T t1);private:double _x;double _y;
};template <class T>
T Point::add(T t1)
{return _x + _y + t1;
}
2.1.6 使用模板的规则
  1. 在一个模块中定义多个通用模板的写法应该谨慎使用;
  2. 调用函数模板时尽量使用隐式调用,让编译器推导出类型;
  3. 无法使用隐式调用的场景只指定必须要指定的类型;

2.2 类模板

一个类模板允许用户为类定义个一种模式,使得类中的某些数据成员、默认成员函数的参数,某些成员函数的返回值,能够取任意类型(包括内置类型和自定义类型)。

如果一个类中的数据成员的数据类型不能确定,或者是某个成员函数的参数或返回值的类型不能确定,就需要将此类声明为模板,它的存在不是代表一个具体的、实际的类,而是代表一类类。

类模板的定义形式如下:

template <class/typename T, ...>
class 类名{
//类定义......
};

实际上,我们之前已经多次见到了类模板,打开c++参考文档,发现vector、set、map等等都是使用类模板定义的。

类模板定义

示例,用类模板的方式实现一个Stack类,可以存放任意类型的数据

——使用函数模板实例化模板函数使用类模板实例化模板类

template <class T, int kCapacity = 10>
class Stack
{
public:Stack(): _top(-1), _data(new T[kCapacity]()){cout << "Stack()" << endl;}~Stack(){if(_data){delete [] _data;_data = nullptr;}cout << "~Stack()" << endl;}bool empty() const;bool full() const;void push(const T &);void pop();T top();
private:int _top;T * _data;
};

类模板的成员函数如果放在类模板定义之外进行实现,需要注意

(1)需要带上template模板形参列表(如果有默认参数,此处不要写,写在声明时就够了)

(2)在添加作用域限定时需要写上完整的类名和模板实参列表

template <class T, int kCapacity>
bool Stack<T,kCapacity>::empty() const{return _top == -1;
}

定义了这样一个类模板后,就可以去创建存放各种类型元素的栈

void test() {Stack<string, 20> ss;Stack<int> st;Stack<> st2;
}

2.3 可变参数模板

可变参数模板(variadic templates)是 C++11 新增的最强大的特性之一,它对参数进行了高度泛化,它能表示0到任意个数、任意类型的参数。由于可变参数模板比较抽象,使用起来需要一定的技巧,所以它也是 C++11 中最难理解和掌握的特性之一。

回想一下C语言中的printf函数,其实是比较特殊的。printf函数的参数个数可能有很多个,用…表示,参数的个数、类型、顺序可以随意,可以写0到任意多个参数。

可变参数模板和普通模板的语义是一样的,只是写法上稍有区别,声明可变参数模板时需要在typename 或 class 后面带上省略号 “…”

template <class ...Args>  
void func(Args ...args);//普通函数模板做对比
template <class T1,class T2>
void func(T1 t1, T2 t2);

Args叫做模板参数包,相当于将 T1/T2/T3/…等类型参数打了包

args叫做函数参数包,相当于将 t1/t2/t3/…等函数参数打了包

省略号写在参数包的左边,代表打包

例如,我们在定义一个函数时,可能有很多个不同类型的参数,不适合一一写出,就可以使用可变参数模板的方法。

——试验:希望打印出传入的参数的内容

就需要对参数包进行解包。每次解出第一个参数,然后递归调用函数模板,直到递归出口

#include <iostream>
using std::cout;
using std::endl;
// 递归的出口
void print() {cout << endl;
}// 可变参数模板
template <class T, class ...Args>
void print(T x, Args ...args) {cout << x << " ";print(args...);	// 省略号写在参数包的右边,代表解包
}int main()
{print(1, "hello", 1.34, "world");return 0;
}

省略号写在参数包的右边,代表解包

如果没有准备递归的出口,那么在可变参数模板中解包解到print()时,不知道该调用什么,因为这个模板至少需要一个参数。

递归的出口可以使用普通函数或者普通的函数模板,但是规范操作是使用普通函数

(1)尽量避免函数模板之间的重载;

(2)普通函数的优先级一定高于函数模板,更不容易出错。

相关文章:

C++模板笔记

Cpp模板笔记 文章目录 Cpp模板笔记1. 为什么要定义模板2. 模板的定义2.1 函数模板2.1.1 函数模板的重载2.1.2 头文件与实现文件形式&#xff08;重要&#xff09;2.1.3 模板的特化2.1.4 模板的参数类型2.1.5 成员函数模板2.1.6 使用模板的规则 2.2 类模板2.3 可变参数模板 模板…...

二极管的动态特性

主要内容 二极管的单向导电特性并不十分理想&#xff0c;这是因为二极管的本质是有P型半导体和N型半导体接触形成的PN结。 PN结除了除了构成单向到点的二极管外&#xff0c;还存在一个结电容&#xff0c;这个结电容会导致"双向"导电。 也就是说&#xff0c;这会让二…...

WSL(Windows Subsystem for Linux)入门

目录 1.简介2.安装与配置3.常用命令4.进阶使用4.1 文件系统交互4.2 网络互通4.3 配置代理4.4 运行 GUI 程序4.5 Docker 集成 1.简介 WSL 是 Windows 系统内置的 Linux 兼容层&#xff0c;允许直接在 Windows 中运行 Linux 命令行工具和应用程序&#xff0c;无需虚拟机或双系统…...

k8s术语之secret

在kubernetes中&#xff0c;还存在一种和ConfigMap非常类似的对象&#xff0c;称之为Secret对象。它主要用于存储敏感信息&#xff0c;例如密码、密钥、证书等等。 首先使用base64对数据进行编码 rootmaster pvs ]# echo -n admin | base64 YWRtaW4 实例&#xff1a;隐藏mysql密…...

Vue2 中 el-dialog 封装组件属性不生效的深度解析(附 $attrs、inheritAttrs 原理)

Vue2 中 el-dialog 封装组件属性不生效的深度解析&#xff08;附 $attrs、inheritAttrs 原理&#xff09; 在使用 Vue2 和 Element UI 进行组件封装时&#xff0c;我们常会遇到父组件传入的属性不生效的情况&#xff0c;比如在封装的 el-dialog 组件中传入 width"100%&qu…...

使用Compose编排工具搭建Ghost博客系统

序&#xff1a;需要提前自备一台部署好docker环境的虚拟机&#xff0c;了解并熟练compose编排工具 在Centos7中&#xff0c;在线/离线安装Docker&#xff1a;https://blog.csdn.net/2301_82085712/article/details/147140694 Docker编排工具---Compose的概述及使用&#xff1…...

车载网络TOP20核心概念科普

一、基础协议与总线技术 CAN总线 定义&#xff1a;控制器局域网&#xff0c;采用差分信号传输&#xff0c;速率最高1Mbps&#xff0c;适用于实时控制&#xff08;如动力系统&#xff09;。形象比喻&#xff1a;如同“神经系统”&#xff0c;负责传递关键控制信号。 LIN总线 定…...

机器学习第一讲:机器学习本质:让机器通过数据自动寻找规律

机器学习第一讲&#xff1a;机器学习本质&#xff1a;让机器通过数据自动寻找规律 资料取自《零基础学机器学习》。 查看总目录&#xff1a;学习大纲 一、从婴儿学说话说起 &#x1f476; 想象你教1岁宝宝认「狗」&#xff1a; 第一阶段&#xff1a;指着不同形态的狗&#x…...

Android ImageView 加载 Base64编码图片

在 Android 中显示服务端返回的 Base64 编码的 GIF 图片&#xff08;如 data:image/gif;base64,...&#xff09;&#xff0c;需要以下步骤&#xff1a; 首先从字符串中分离出纯 Base64 部分&#xff08;去掉 data:image/gif;base64, 前缀&#xff09;image/gif 表示图片是 gif…...

如何使用 QuickAPI 推动医院数据共享 —— 基于数据仓库场景的实践

目录 01 医疗行业面临的数据孤岛问题 02 QuickAPI&#xff1a;将 SQL 变为数据 API 的利器 03 快速落地的应用实践 ✅ 步骤一&#xff1a;统一 SQL 逻辑&#xff0c;模块化管理 ✅ 步骤二&#xff1a;配置权限与调用策略 ✅ 步骤三&#xff1a;上线并接入调用 04 核心收…...

【5G通信】bwp和redcap 随手记 2

好的&#xff0c;让我们重新解释Cell-Defined BWP (CD BWP) 和 Non-Cell-Defined BWP (NCD BWP)&#xff0c;并结合RedCap终端和非RedCap终端的应用进行说明。 一、定义 Cell-Defined BWP (CD BWP) 定义&#xff1a;由网络&#xff08;基站&#xff09;通过RRC信令为终端配置的…...

AI时代企业应用系统架构的新思路与CIO变革指南

作为制造企业CIO&#xff0c;我们看问题需要有前瞻性&#xff0c;AI时代企业应用系统架构需要进行全面转型。 一、新思想与新技术 1. 核心新思想 可视化开发AI的融合模式&#xff1a;不再只依赖纯代码开发或传统低代码&#xff0c;而是两者结合&#xff0c;通过AI理解自然语…...

kotlin JvmName注解的作用和用途

1. JvmName 注解的作用 JvmName 是 Kotlin 提供的一个注解&#xff0c;用于在编译为 Java 字节码时自定义生成的类名或方法名。 作用对象&#xff1a; 文件级别&#xff08;整个 .kt 文件&#xff09;函数、属性、类等成员 主要用途&#xff1a; 控制 Kotlin 编译后生成的 JV…...

为什么强调 RESTful 的无状态性?-优雅草卓伊凡

为什么强调 RESTful 的无状态性&#xff1f;-优雅草卓伊凡 RESTful 架构的核心原则之一是 无状态性&#xff08;Statelessness&#xff09;&#xff0c;它要求 每次客户端请求必须包含服务器处理该请求所需的所有信息&#xff0c;服务器不会存储客户端的状态&#xff08;如会话…...

信息系统项目管理工程师备考计算类真题讲解十五

一、决策论问题 分析&#xff1a;首先要明白几个概念&#xff1a; 1&#xff09;最大最大准则&#xff08;Maxmax&#xff09;:也称乐观主义准则&#xff0c;其决策原则为“大中取大” 2&#xff09;最大最小准则&#xff08;Maxmin&#xff09;:也称悲观主义准则&#xff0c…...

android-ndk开发(9): undefined reference to `__aarch64_ldadd4_acq_rel` 报错分析

1. 概要 基础库 libbase.a 基于 android ndk r18b 编译&#xff0c; 被算法库 libfoo.so 和算法库 libbar.a 依赖&#xff0c; 算法库则分别被 libapp1.so 和 libapp2.so 依赖。 libapp1.so 的开发者向 libfoo.so 的开发者反馈了链接报错&#xff1a; error: undefined symb…...

Java 对象克隆(Object Cloning)详解

Java 对象克隆(Object Cloning)详解 对象克隆是指创建一个对象的精确副本,Java 提供了两种克隆方式:浅克隆(Shallow Clone)和深克隆(Deep Clone)。下面从实现原理、使用场景到注意事项全面解析。 一、克隆的基本概念 1. 为什么要克隆? 需要对象副本时避免修改原始对…...

Asp.Net Core IIS发布后PUT、DELETE请求错误405

一、方案1 1、IIS管理器&#xff0c;处理程序映射。 2、找到aspNetCore&#xff0c;双击。点击请求限制...按钮&#xff0c;并在谓词选项卡上&#xff0c;添加两者DELETE和PUT. 二、方案2 打开web.config文件&#xff0c;添加<remove name"WebDAVModule" />&…...

AI搜索的未来:技术纵深发展与关键突破路径

一、模型架构的颠覆性重构 ‌1、混合专家系统&#xff08;MoE&#xff09;的工程化突破‌ ‌动态路径选择‌&#xff1a;Google Gemini 1.5 Pro采用MoE架构&#xff0c;其门控网络(Gating Network)通过实时计算输入token与128个专家模型的余弦相似度&#xff0c;动态分配计算资…...

从艾米・阿尔文看 CTO 的多面特质与成长路径

在《对话 CTO&#xff0c;驾驭高科技浪潮》的开篇&#xff0c;艾米・阿尔文的经历如同一扇窗&#xff0c;为我们展现出首席技术官丰富而立体的世界。通过深入探究这一章节&#xff0c;我们能洞察 CTO 在技术领域前行所需的特质、面临的挑战&#xff0c;以及成长发展的脉络。 一…...

记录阿里云服务器搭建FTP服务器的注意事项

在阿里云服务器上&#xff08;centos&#xff09;系统&#xff0c;使用vsftpd搭建了一台FTP服务器。 搭建过程中&#xff0c;也留意到了操作防火墙放行端口。但搭建成功后&#xff0c;仍无法访问。 问题是&#xff1a;还需要在阿里云控制台设置一下。 在此记录。 1、登陆账…...

第二章 Logback的架构(三)

Logger, Appenders 和 Layouts 工作原理概述 在介绍了基本的Logback组件之后&#xff0c;我们现在可以描述当用户调用Logger的打印方法时&#xff0c;Logback框架日志请求的执行步骤。 现在让我们分析一下当用户调用名为com.wombat的Logger的info()方法时&#xff0c;Logback…...

第十六届蓝桥杯大赛软件赛C/C++大学B组部分题解

第十六届蓝桥杯大赛软件赛C/C大学B组题解 试题A: 移动距离 问题描述 小明初始在二维平面的原点&#xff0c;他想前往坐标(233,666)。在移动过程中&#xff0c;他只能采用以下两种移动方式&#xff0c;并且这两种移动方式可以交替、不限次数地使用&#xff1a; 水平向右移动…...

服务器数据恢复—Linux操作系统服务器意外断电导致部分文件丢失的数据恢复

服务器数据恢复环境&故障&#xff1a; 一台安装linux系统的服务器意外断电。管理员重启服务器后进行检测&#xff0c;发现服务器上部分文件丢失。管理员没有进行任何操作&#xff0c;直接将服务器正常关机并切断电源。 服务器数据恢复过程&#xff1a; 1、北亚企安数据恢复…...

技术视界 | 青龙机器人训练地形详解(三):复杂地形精讲之台阶

在前两篇中&#xff0c;我们依次讲解了“如何创建一个地形”以及“如何将地形添加到训练环境中”。从基础出发&#xff0c;逐步构建机器人可交互的三维仿真环境。在机器人强化学习训练中&#xff0c;地形的复杂度决定了策略的泛化能力&#xff0c;仅靠 jump_plat 和 jump_pit 等…...

Android 位掩码操作(和~和|的二进制运算)

在 Android 开发中&#xff0c;位掩码操作通过二进制位的逻辑运算实现高效的状态管理。以下以 &&#xff08;与&#xff09;、|&#xff08;或&#xff09;和 ~&#xff08;非&#xff09;运算符为例&#xff0c;详细说明其二进制计算过程&#xff1a; 一、按位与 & 运…...

【JS逆向基础】前端基础-HTML与CSS

1&#xff0c;flask框架 以下是一个使用flask框架写成的serve程序 # noinspection PyUnresolvedReferences #Flash框架的基本内容from flask import Flask app Flask(__name__)app.route(/index) def index():return "hello index"app.route(/login) def login():re…...

高速供电,一步到位——以太联-Intellinet 9口2.5G PoE++非管理型交换机_562140:网络升级的理想之选

在数字化浪潮席卷全球的当下&#xff0c;高速稳定的网络连接已成为企业运营、家庭娱乐以及各类智能场景正常运转的基石。从企业办公场景中员工对高效协同办公的追求&#xff0c;到家庭环境里用户对流畅高清视频、在线游戏的渴望&#xff0c;再到智慧城市建设中大量监控设备、无…...

rom定制系列------红米note12 5G版miui14修改型号root版 原生安卓14批量线刷固件 原生安卓15等

红米Note 12 5G机型也称为 Note 12R Pro&#xff0c;机型代码&#xff1a;sunstone 高通骁龙4 Gen1八核处理器适用于以下型号的小米机型&#xff1a;22111317G, 22111317I, 22101317C miui14稳定版 14.0.10安卓13固件 根据客户需求&#xff0c;采用miui最后一个版本。修改以…...

机器学习 数据集

数据集 1. scikit-learn工具介绍1.1 scikit-learn安装1.2 Scikit-learn包含的内容 2 数据集2.1 sklearn玩具数据集介绍2.2 sklearn现实世界数据集介绍2.3 sklearn加载玩具数据集示例1&#xff1a;鸢尾花数据示例2&#xff1a;分析糖尿病数据集 2.4 sklearn获取现实世界数据集示…...

JVM运行时数据区域(Run-Time Data Areas)的解析

# JVM运行时数据区域(Run-Time Data Areas)的解析 欢迎来到我的博客&#xff1a;TWind的博客 我的CSDN:&#xff1a;Thanwind-CSDN博客 我的掘金&#xff1a;Thanwinde 的个人主页 本文参考于&#xff1a;深入理解Java虚拟机&#xff1a;JVM高级特性与最佳实践 本文的JVM均…...

python基础:序列和索引-->Python的特殊属性

一.序列和索引 1.1 用索引检索字符串中的元素 # 正向递增 shelloworld for i in range (0,len(s)):# i是索引print(i,s[i],end\t\t) print(\n--------------------------) # 反向递减 for i in range (-10,0):print(i,s[i],end\t\t)print(\n--------------------------) print(…...

在k8s中,如何实现服务的访问,k8s的ip是变化的,怎么保证能访问到我的服务

在K8S中&#xff0c;Pod的IP动态变化确实无法直接通过固定IP限制访问&#xff0c;但可以通过标签&#xff08;Label&#xff09;、服务&#xff08;Service&#xff09;和网络策略&#xff08;NetworkPolicy&#xff09;的组合&#xff0c;实现动态身份识别的访问控制&#xff…...

用NVivo革新企业创新:洞悉市场情绪,引领金融未来

在当今快速变化的商业环境中&#xff0c;理解市场和客户的情感脉动是企业成功的关键。尤其在金融行业&#xff0c;无论是评估经济走势、股票市场波动&#xff0c;还是洞察消费者信心&#xff0c;精准把握隐藏在新闻报道、社交媒体和消费者反馈中的情感倾向至关重要。而NVivo这款…...

如何使用极狐GitLab 软件包仓库功能托管 helm chart?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 软件包库中的 Helm charts (BASIC ALL) WARNING:Helm chart 库正在开发中&#xff0c;由于功能有限&#xff0c;尚未准备好用…...

Qt 通过控件按钮实现hello world + 命名规范(7)

文章目录 使用编辑框来完成 hello world通过编辑图形化界面方式通过纯代码方式 通过按钮的方式来创建 hello world通过编辑图形化界面方式通过纯代码方式 总结Qt Creator中的快捷键如何使用文档命名规范 简介&#xff1a;这篇文章着重点并不在于创建hello world程序&#xff0c…...

uniapp index.html怎么改都不生效

打开 manifest.json index.html 模板路径默认为空&#xff0c;所以你改的 index.html 是没用的&#xff0c;uni-app 根本没用这个模板 设置模板后就会生效了...

ABP vNext + gRPC 实现服务间高速通信

ABP vNext gRPC 实现服务间高速通信 &#x1f4a8; 在现代微服务架构中&#xff0c;服务之间频繁的调用往往对性能构成挑战。尤其在电商秒杀、金融风控、实时监控等对响应延迟敏感的场景中&#xff0c;传统 REST API 面临序列化负担重、数据体积大、通信延迟高等瓶颈。 本文…...

【JAVA】十三、基础知识“接口”精细讲解!(三)(新手友好版~)

目录 1. Object类 1.1 Object的概念 1.2 Object例子 2. toString 2.1 toString的概念 2.2 为什么要重写toString 2.3 如何重写toString 3. 对象比较equals方法 3.1 equals( ) 方法的概念 3.2 Object类中的默认equals实现 3.3 如何正确重写equals方法 4. hashCode方…...

每周靶点分享:Angptl3、IgE、ADAM9及文献分享:抗体的多样性和特异性以及结构的新见解

本期精选了《脂质代谢的关键调控者Angptl3》《T细胞活化抑制因子VISTA靶点》《文献分享&#xff1a;双特异性抗体重轻链配对设计》三篇文章。以下为各研究内容的概述&#xff1a; 1. 脂质代谢的关键调控者Angptl3 血管生成素相关蛋白3&#xff08;Angptl3&#xff09;是血管生…...

网络协议之DHCP和PXE分析

写在前面 本文看下DHCP和PXE相关内容。 1&#xff1a;正文 不知道你自己手动配置过IP地址没有&#xff0c;在Linux的环境中可以通过如下的命令们来进行配置&#xff1a; $ sudo ifconfig eth1 10.0.0.1/24 $ sudo ifconfig eth1 up以及&#xff1a;$ sudo ip addr add 10.0…...

SSH 服务部署指南

本指南涵盖 OpenSSH 服务端的安装、配置密码/公钥/多因素认证&#xff0c;以及连接测试方法。 适用系统&#xff1a;Ubuntu/Debian、CentOS/RHEL 等主流 Linux 发行版。 1. 安装 SSH 服务端 Ubuntu/Debian # 更新软件包索引 sudo apt update# 安装 OpenSSH 服务端 sudo apt i…...

表达式求值(算法题)

#include <bits/stdc.h> // 引入常用头文件 using namespace std;stack<int> num; // 存储操作数的栈 stack<char> op; // 存储运算符的栈/* 执行一次运算操作&#xff1a;1. 从num栈弹出两个操作数(n2先弹出&#xff0c;作为右操作数)2. 从op栈弹出运算符…...

IO流--13--MultipartFile

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 MultipartFile1. 概述2. 常用方法解析2.1 getName方法2.2 getOriginalFileName方法2.3 getContentType方法2.4 isEmpty方法2.5 getSize方法2.6 getBytes方法2.7 get…...

leetcode 242. Valid Anagram

题目描述 因为s和t仅仅包含小写字母&#xff0c;所以可以开一个26个元素的数组用来做哈希表。不过如果是unicode字符&#xff0c;那就用编程语言自带的哈希表。 class Solution { public:bool isAnagram(string s, string t) {int n s.size();if(s.size() ! t.size())return …...

内核态函数strlcpy及strscpy以及用户态函数strncpy

一、背景 编写C程序时有一类看似简单实则经常暗藏漏洞的问题就是字符串的处理。对于字符串的处理&#xff0c;常用的函数如strcpy&#xff0c;sprintf&#xff0c;strcat等&#xff0c;这些函数的区别无外乎就是处理\0结尾相关的逻辑。字符串的长度有时候并不能很好确定&#…...

Matlab 车辆四自由度垂向模型平稳性

1、内容简介 Matlab221-车辆四自由度垂向模型平稳性 可以交流、咨询、答疑 2、内容说明 略 3、仿真分析 略 4、参考论文 略基于Simulink的汽车平顺性仿真_1_杜充 基于Simulink的汽车平顺性仿真分析_谢俊淋...

【hadoop】Sqoop数据迁移工具的安装部署

一、Sqoop安装与配置 步骤&#xff1a; 1、使用XFTP将Sqoop安装包sqoop-1.4.6.bin__hadoop-2.0.4-alpha.tar.gz发送到master机器的主目录。 2、解压安装包&#xff1a; tar -zxvf ~/sqoop-1.4.6.bin__hadoop-2.0.4-alpha.tar.gz 3、修改文件夹的名字&#xff0c;将其改为s…...

只出现一次的数字(暴力、哈希查重、异或运算)

目录 一.题目 题目解析 题目链接 二.解题过程 俗手&#xff08;暴力&#xff1a;数组模拟哈希表&#xff09; 思路 代码示例 提交情况 本手&#xff1a;哈希查重 思路 代码示例 提交情况 妙手&#xff1a;异或运算 思路 代码示例 提交情况 作者的个人gitee 作者…...

Spark缓存

生活中缓存容量受成本和体积限制&#xff08;比如 CPU 缓存只有几 MB 到几十 MB&#xff09;&#xff0c;但会通过算法&#xff08;如 “最近最少使用” 原则&#xff09;智能决定存什么&#xff0c;确保存的是 “最可能被用到的数据”。 1. 为什么需要缓存&#xff1f; 惰性执…...