11 类型泛化

11 类型泛化

  • 1、函数模版
    • 1.1 前言
    • 1.2 函数模版
    • 1.3 隐式推断类型实参
    • 1.4 函数模板重载
    • 1.5 函数模板类型形参的默认类型(C++11标准)
  • 2、类模版
    • 2.1 类模板的成员函数延迟实例化
    • 2.2 类模板的静态成员
    • 2.3 类模板的递归实例化
    • 2.4 类模板类型形参缺省值
  • 3、类模板的扩展
    • 3.1 模板型成员变量
    • 3.2 模板型成员函数
    • 3.3 模板型成员类型
    • 3.4 类模板中成员虚函数
  • 4、模板的特殊用法
    • 4.1 数值型的类型形参
    • 4.2 模板型的类型形参
  • 5、模板的经典错误(补)
    • 5.1 模板二次编译
    • 5.2 嵌套依赖
    • 5.3 利用类型形参调用成员函数模板
    • 5.4 子类模板访问基类模板

1、函数模版

1.1 前言

首先C++为强类型语言
-优点:有很多的数据类型(基本类型、类类型)在类型安全性方面是无可比拟的
-缺点:因为数据类型的多样性,在很大程度上会给程序员编写通用代码带来麻烦
这就使得程序员需要为每一种数据类型编写完全相同或者近乎完全相同的代码,即便它们在抽象层面是一致的。

int Max(int x, int y){return x > y ? x : y;
}
double Max(double x, double y){return x > y ? x : y;
}string Max(string x, string y){return x > y ? x : y;
}
int main(){int nx = 10, ny = 11;cout << Max(nx, ny) << endl;double dx = 1.0, dy = 1.1;cout << Max(dx, dy) << endl;string sx = "1.0", sy = "1.1";cout << Max(sx, sy) << endl;...如果有其他类型的比较大小,我们需要为每一种类型编写相同的代码
}

1.2 函数模版

  • 定义语法:
template<class|typename 类型形参1,class|typename类型形参2,...>返回值类型 函数模板名(调用形参1,调用形参2,…){...}示例:
template<typename T>T Max(T x,Ty){return x y?x:y;
}

注意:可以使用任何标识符作为类型形参的名称,但使用“T”己经称为一种惯例,“T”表示的是,调用者在使用函数模板时指定的任意类型

  • 使用:使用函数模板必须对函数模板进行实例化
    • 语法
函数模板名<类型实参1,类型实参2,>(调用实参1…);
  • 示例
// 定义函数模板
template<typename T>T Min(T x, T y){return x > y ? y : x;
}int main(){int nx = 10, ny = 11;// 使用函数模板cout << Min<int>(nx, ny) << endl;double dx = 1.0, dy = 1.1;cout << Min<double>(dx, dy) << endl;string sx = "1.0", sy = "1.1";cout << Min<string>(sx, sy) << endl;return 0;
}
  • 分析
    • 编译器并没有把函数模板翻译成一个可以处理任何数据类型的单一实体
    • 编译器在实例化函数模板时根据类型实参从函数模板中产生一个真正的函数实体
    • 函数模板并不是一个函数实体,通过实例化才能产生真正的函数实体
    • 函数模板可以看成是编译器生成函数实体的一个依据而已
    • 这种用具体数据类型替换函数模板类型形参的过程叫做实例化,这个过程将产生一个函数模板的实例(函数实体)
    • 原则上来说可以使用任何类型来实例化函数模板,不管其为基本类型还是类类型但前提是这个类型必须支持函数模板所要执行的操作

1.3 隐式推断类型实参

  • 概念
    • 若函数模板的调用形参和类型形参相关,那么在实例化函数模板时即使不显式指明类型实参,编译器也有能力根据调用实参的类型隐式推断出类型实参的类型
    • 获得和调用普通函数一致的书写形式
template<typename T>T Min(T x, T y){return x > y ? y : x;
}
int main(){int nx = 10, ny = 11;// 调用形参和类型形参相关cout << Min(nx, ny) << endl;// --> cout << Min<int>(nx, ny) << endl;double dx = 1.0, dy = 1.1;cout << Min(dx, dy) << endl;string sx = "1.0", sy = "1.1";cout << Min(sx, sy) << endl;return 0;
}
  • 三种情况不能做隐式推断
    1: 调用参数和类型参数不完全相关
template  <typename T, typename D>void Foo(D x){cout << x << endl;
}
int main(){int nx = 10, ny = 11;//Foo(nx);// 不能推断出T的类型Foo<float>(nx);return 0;
}

2: 隐式推断类型实参不能同时隐式类型转换

template  <typename T>void Foo1(T x,T y){cout << x << endl;
}
int main(){// Foo1(11,13.5);// 隐式推断类型实参不能同时隐式类型转换Foo1(11, (int)13.5);return 0;
}

3: 返回值类型不能隐式推断

template<typename R,typename T>
R Bar(T x){R r;return t;
}
// 返回值类型不能隐式推断
void C11_06(){int a = 10;//Bar(10);// 根据实参能推断出形参T的类型,但是无法推断出R的类型Bar<float>(10); 
}

1.4 函数模板重载

  • 普通函数能够实例化出该函数的函数模板构成重载关系
    • 在调用参数类型匹配度相同情况下编译器优先选择普通函数
    • 除非函数模板可以产生调用参数类型匹配度更好的函数
  • 隐式推断类型实参不能同时隐式类型转换但普通函数可以,如果在传递参数时如果需要编译器做隐式类型转换,则编译器选择普通函数。
  • 可以在实例化时用<>强行通知编译器选择函数模板。
void MaxType(int x, int y){cout <<"1.MaxType" << endl;
}
template<typename T>void MaxType(T x, T y){cout << "2.MaxType" << endl;
}int main(){int nx = 10, ny = 11;MaxType(nx,ny); // 调用普通函数  在调用参数类型匹配度相同情况下编译器优先选择普通函数double dx = 1.0, dy = 1.1;MaxType(dx, dy);// 调用函数模版 函数模板可以产生调用参数类型匹配度更好的函数MaxType(nx,dy);// 调用函数模版 隐式推断类型实参不能同时隐式类型转换但普通函数可以MaxType<>(nx, ny);// <>强行通知编译器选择函数模板。return 0;
}

1.5 函数模板类型形参的默认类型(C++11标准)

  • 函数模板的类型形参可以带有默认类型
    • 在实例化函数模板时,如果提供了类型实参则用所提供的类型实参来实例化函数模板
    • 在实例化函数模板时,如果没提供类型实参则用类型形参的缺省类型来实例化函数模板
    • 如果某一个类型形参带有缺省类型,则其后的类型形参都必须带有缺省类型
template  <typename T=short, typename D=float>void Foo2(int x=0){T t;D d;cout << "t的类型" << typeid(t).name() << " d的类型" << typeid(d).name() << endl;
}
int main(){Foo2<int, double>(100);// 用提供的类型Foo2(100);// 用默认类型return 0;
}

2、类模版

  • 形式:
// 书写
template<tyepname类型形参1,...>
class 类模板名{...
}
// 示例
template<typename A,typename B>
class CMath{
public:A m a;B func0{…}
}
  • 使用:类模板必须对类模板进行实例化(产生真正的
    • 类模板本身并不代表一个确定的类型(即不能用于定义对象
    • 只有通过类型实参实例化成真正的类后才具备类的特性(即可以定义对象)。
template<typename T>
class CMath{
public:CMath(T t1, T t2) :m_t1(t1), m_t2(t2){}T add(){ return m_t1 + m_t2; }T sub();// 声明
private:T m_t1;T m_t2;
};
// T sub()定义 template<typename T>不能丢,要用类名引成员,切记不用要类模板引成员
template<typename T>
T CMath<T>::sub(){return m_t1 - m_t2;
}int main(){int nx = 10, ny = 11;CMath<int>m1(nx, ny);cout << m1.add() << endl;double dx = 1.0, dy = 1.1;CMath<double>m2(dx, dy);cout << m2.add() << endl;string sx = "1.0", sy = "1.1";CMath<string>m3(sx, sy);cout << m3.add() << endl;return 0;
}

2.1 类模板的成员函数延迟实例化

类模板被实例化产生真正类的一刻,类模板中的成员函数并没有实例化, 成员函数只有在被调用时才会被实例化(即产生真正成员函数),称之为成员函数的延迟实例化
注意:成员虚函数除外
某些类型虽然并没有提供类模板所需要的全部功能,但照样可以用它来实例化类模板,只要不调用那些未提供功能的成员函数即可。

int main(){// CMath<int> 编译到这是,类中只有成员变量,没有成员函数// CMath<int>m1(nx,ny) // 此时编译器才会给类中添加构造函数// m1.add()  // 此时才会去类中添加add的成员函数return 0;
}

2.2 类模板的静态成员

  • 类模板中的静态成员既不是每个对象拥有一份也不是类模板拥有一份,应该是由类模板实例化出的每一个真正的类各自拥有一份,且为该实例化类定义的所有对象共享
template <typename T> class A
{
public:static T m_t;// 声明static void foo(){cout << "A<T>::foo()" << endl;}
};
template <typename T> T A <T> ::m_t=T();// 定义 T()零值初始化
// 类模板中的静态成员即不是每个对象拥有一份也不是类模板拥有一份,
// 应该是由类模板实例化出的每一个真正的类各自拥有一份,且为该实例化类定义的所有对象共享
int main(){A<int> x, y;cout << "&A<int>::m_t:" << &A<int>::m_t << endl; // &A<int>::m_t:00DB53F8cout << "&x.m_t:" << &x.m_t << " &y.m_t:" << &y.m_t << endl; //&x.m_t:00DB53F8 &y.m_t:00DB53F8A<double> m, n;cout << "&A<double>::m_t:" << &A<double>::m_t << endl; // &A<double>::m_t:00DB5400cout << "&m.m_t:" << &m.m_t << " &m.m_t:" << &m.m_t << endl;// &m.m_t:00DB5400 &m.m_t:00DB5400return 0;
}

2.3 类模板的递归实例化

  • 概念
    • 利用类模板实例化产生的类来实例化类模板自身,这种做法称之为类模板的递归实例化
  • 应用
    • 通过这种方法可以构建空间上具有递归特性的数据结构
      (例如:多维数组)
  • 示例
    • Array<Array<int> >
template<typename T>class Array{
public:T& operator[](size_t i){return arr[i];}
private:T arr[10];
};
# include <iomanip>
int main(){Array<int> a; // a对象当成是一维数组看待for (int i = 0; i < 10; i++){a[i] = 1000 + i;}for (int i = 0; i < 10; i++){cout << a[i] << " ";}cout << endl;Array< Array<int> > b; // 类模板的递归实例化for (int i = 0; i < 10; i++){for (int j = 0; j < 10; j++){b[i][j] = i*j;}}for (int i = 1; i < 10; i++){for (int j = 1; j <= i; j++){cout << i << "X" << j << "=";cout << left << setw(2) << setfill(' ') << b[i][j] << " ";}cout << endl;}return 0;
}

2.4 类模板类型形参缺省值

  • 类模板的类型形参可以带有缺省类型
    • 在实例化类模板时,如果提供了类型实参则用所提供的类型实参来实例化类模板
    • 在实例化类模板时,如果没提供类型实参则用类型形参的缺省类型来实例化类模板
    • 如果某一个类型形参带有缺省类型,则其后的类型形参都必须带有缺省类型
template<typename T=short,typename D=int>
class CCMath{
private:T m_t;D m_d;
public:void print(){cout << "m_t的类型:" << typeid(m_t).name() << endl;cout << "m_d的类型:" << typeid(m_d).name() << endl;}
};int main(){CCMath<>m1;CCMath<int>m2;CCMath<int,double>m3;m1.print();m2.print();m3.print();return 0;
}

3、类模板的扩展

3.1 模板型成员变量

成员变量,但其类型是由类模板实例化的未知类,称之为模板型成员变量

  • 示例
template <typename T> class Array{...}
template <typename D> class Sum{
public :Array<D> m_d;	// 模板型成员变量
}
template<typename T>class Array2{
public:T& operator[](size_t i){return arr[i];}
private:T arr[10];
};
template<typename D>class Sum{
public:Sum(const Array2<D>& v) :m_a(v){}D add(){ // 求和器D d = D();for (int i = 0; i < 10; i++){d += m_a[i];}return d;}
private:Array2<D> m_a;//模板型成员变量
};int main(){Array2<int> a;for (int i = 0; i < 10; i++){a[i] = i+1;}Sum<int> s(a);cout << s.add() << endl;return 0;
}

3.2 模板型成员函数

模板型成员函数 又名 成员函数模板
示例:

template<typename T> class BB{
public:template<typename D>void foo(){ // 成员函数模板cout << "AA::foo<D>" << endl;}template<typename D>void bar();// 声明
};
template<typename T> template<typename D> void BB<T>::bar(){// 定义cout << "BB :: bar" << endl;
};void C11_15(){AA a;a.foo<int>();BB<int>b; // 对类模板实例化b.foo<int>();// 对函数模板实例化b.bar<int>();
}

3.3 模板型成员类型

template<typename X>class A1{
public:template<typename Y>class B1{public:template<typename X> void foo(){ // 模板型成员类型cout << "foo() " << endl;}};
};
int main(){A1<int>::B1<int>b;b.foo<int>();return 0;
}

3.4 类模板中成员虚函数

template <typename T> class Base{
public:virtual void foo(){//虚函数cout << "Base<T>::foo" << endl;}
};
// 只有在父子类中,才可以将模板进行合并
template<typename T, typename D>class Derived :public Base<T>{
public:void foo(){cout << "Derived<T,D>::foo" << endl;}
};
int main(){Derived<int, double> dr;Base<int>* pdase = &dr;pdase->foo();return 0;
}

注意:类模板中可以有虚函数,也可以表现出多态性,但是不能有成员虚函数模板
这是因为根据成员虚函数的多态机制,需要一个虚函数表(表中保存成员虚函数的入口地址),而这个表是编译器在实例化类模板时就创建,而成员函数模板的实例化(即产生真正的函数实体)需要编译器处理完并且调用后才会完成,这时才出现成员虚函数的地址,所以成员函数模板的延迟实例化,阻碍了虚函数表的构建

4、模板的特殊用法

4.1 数值型的类型形参

模板的类型形参也可以是数值类型(只能是整数),可以有缺省值

template<typename T,int S=10>class Array{
public:T& operator[](size_t i){return arr[i];}int size(){return S;}
private:T arr[S];
};
int main(){Array<int, 30> a;for (int i = 0; i < a.size(); i++){a[i] = 1000 + i;}for (int i = 0; i < a.size(); i++){cout << a[i] << ' ';}return 0;
}

4.2 模板型的类型形参

模板的类型形参也可以是类模版,可以有缺省值

template<typename T>class Array{
public:T& operator[](size_t i){return arr[i];}int size(){return 10;}
private:T arr[10];
};
template<typename D, template<typename M> class C = Array> class SumArray{
public:SumArray(const C<D>& v) :m_a(v){};D add(){D a = D();for (int i = 0; i < m_a.size(); i++){a += m_a[i];}return a;}
private:C<D> m_a;
};
int main(){Array<int> a;for (int i = 0; i < a.size(); i++){a[i] = 1000 + i;}SumArray<int, Array> s(a);cout << s.add() << endl;
}

5、模板的经典错误(补)

5.1 模板二次编译

如果都过不了第一次编译,那么就谈不上第二次编译

  • 编译器对模板会进行两次编译
  • 第一次编译:发生在实例化模板之前(产生真正函数或真正类之前)只检查模板本身内部代码(只检查基本词法是否正确)
    • 模板内部出现的所有标识符是否均有声明
    • 对于已知类型的调用要检查调用是否有效
    • 对于未知类型调用认为都合理
  • 第二次编译:第二次编译发生在实例化模板之后(产生真正函数或真正类之后)结合所使用的类型实参,再次检查模板代码,查看所有调用是否真的都有效
template<typename T>void func(){// 第一次编译时,编译器针对未知类型调用采用隐忍态度,尽量认为都合理//fdf; //错误 模板内部出现的所有标识符是否均有声明   第一次编译A3 a; a.foo(); // 对于已知类型的调用要检查调用是否有效 第一次编译//a.fdfddf();// 错误 在A3类中没有fdfddf 第一次编译T t;t.jsjhdeasde();// 第一次编译不会报错,这是因为对于未知类型调用认为都合理  ,第二次编译会报错,这是因为第二次编译时,编译器结合类型实参,再次检查,所有调用是否真的合理// t.fds<fd>fd();// 这种的第一次编译也会报错
}
int main(){func<int>();return 0;
}

5.2 嵌套依赖

  • 问题:由于模板要经过两次编译,在第一次编译模板的代码时,类型形参的具体类型尚不明确,编译器将把类型形参的嵌套类型理解为某个未知类型的静态成员变量,因此编译器看到使用这样的标识符声明变量时会报告错误,这就叫嵌套依赖。
  • 解决办法:在类型形参的前面增加一个typename标识符,意在告诉编译器其后是一个类模板的嵌套使用。
class A{
public:class B{public:void foo(){cout << "A::B::foo" << endl;}};
};
template<typename T>void func1(){
// 第一次编译时,会把T::B看做成B是T的静态成员变量,编译器看到使用这样的标识符声明变量时会报告错误
// T::B 类型形参的嵌套类型   typename 告诉编译器B是T(未知类)的嵌套类型typename T::B b;b.foo();
}
int main(){func1<A>();return 0;
}

5.3 利用类型形参调用成员函数模板

  • 问题:利用未知类定义的对象来访问成员函数模板时,编译器在第一次编译时无法解析成员函数模板的类型参数列表的<>而报告编译错误。
  • 解决方法:在成员函数模板之前增加template关键字,意在告诉编译器其后是一个函数模板实例,编译器就可以正确理解<>了。
class A{
public:template <typename T> void foo(){cout << "A::f00" << endl;}
};
template <typename T> void func2(){T t;// 编译器第一次编译时,未知类型定义的对象,调用方法时,不能带有<>    // template告诉编译器后面的是函数模板的实例化t.template foo<int>(); 
}
int main(){ func2<A>(); return 0;
}

5.4 子类模板访问基类模板

  • 问题:在子类模板中访问基类模板的成员,编译器第一次编译时只在子类模板和全局域中搜索使用的标识符号,不会到基类模板中搜索
  • 解决方法:在子类模板中可以通过使用作用域限定符或显式使用this指针
template <typename T > class BaseA{
public:int m_i;void foo(){ cout << "BaseA<T>::foo()" << endl; }
};
template<typename T, typename D> class Deri : public BaseA<T>{
public:void bar(/*    this*/ ){//m_i = 100; 报错 //foo();BaseA::foo();BaseA::m_i = 100;this->foo();this->m_i = 100;}
};
int main(){Deri<int, int >d;d.bar();return 0;
}

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

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

相关文章

小鹏汽车2025冲刺类L4智驾,挑战与机遇并存

随着科技的飞速发展&#xff0c;智能驾驶已成为汽车行业的前沿领域。近日&#xff0c;小鹏汽车在AI DAY上宣布国内首个量产上车的端到端大模型&#xff0c;这一创新举措无疑为智能驾驶的发展注入了新的活力。然而&#xff0c;在迈向2025年实现类L4级智能驾驶的道路上&#xff0…

大前端 业务架构 插件库 设计模式 属性 线程

大前端 业务架构 插件库 适配模式之(多态)协议1对多 抽象工厂模式 观察者模式 外观模式 装饰模式之参考catagory 策略模式 属性

橡胶油封的用途是什么?

橡胶油封的用途是什么? 在机械工程和设备维护领域&#xff0c;橡胶油封发挥着至关重要的作用&#xff0c;确保各部件的耐用性和效率。那么&#xff0c;橡胶油封的具体用途是什么呢?本文将从多角度探讨橡胶油封的应用和优势&#xff0c;突出其在各个工业和汽车领域中的重要性…

QT 中文乱码 以及 tr 的使用

一、关于显示中文 1、网上常规的做法 - 第一步&#xff1a;代码文件选择用utf8编码带bom。QT Creator 文本编辑 行为配置里可以配置 - 第二步&#xff1a;在有中文汉字的代码文件顶部加一行&#xff08;一般是cpp文件&#xff09; #pragma execution_character_set("utf-…

服务器新硬盘分区、格式化和挂载

文章目录 参考文献查看了一下起点现状分区(base) ~ sudo parted /dev/sdcmklabel gpt&#xff08;设置分区类型&#xff09;增加分区 格式化需要先退出quit&#xff08;可以&#xff09;(base) / sudo mkfs.xfs /dev/sdc/sdc1&#xff08;失败&#xff09;sudo mkfs.xfs /dev/s…

通过nginx转发后应用偶发502bad gateway

序言 学习了一些东西&#xff0c;如何才是真正自己能用的呢&#xff1f;好像就是看自己的潜意识的反应&#xff0c;例如解决了一个问题&#xff0c;那么下次再碰到类似的问题&#xff0c;能直接下意识的去找到对应的信息&#xff0c;从而解决&#xff0c;而不是和第一次碰到一样…

softmax的数值溢出问题

softmax是deep learning常用的一个操作&#xff0c;虽然有很多现成的包可以调&#xff0c;但在某些场景下需要自己实现。本文简单探讨一下softmax可能会出现的数值稳定性问题 解决上溢出问题 Softmax ( x i ) exp ⁡ ( x i ) ∑ j 1 N exp ⁡ ( x j ) exp ⁡ ( x i ) / exp…

CRC循环冗余校验

CRC循环冗余校验 循环冗余校验码是一种用在数字网络和存储设备上的差错校验码&#xff0c;可以校验原始数据的偶然差 错。 CRC 计算单元使用固定多项式计算 32 位 CRC 校验码。 1. 硬件CRC 在单片机中&#xff0c;芯片具有专用的CRC计算单元&#xff0c;它是按照32位数据长…

LeetCode 48.旋转图像

1.做题要求: 2.从此题我们可以看出规律为第几行要变为倒数第几列&#xff0c;所以我们最好先把二维数组存入一维数组中&#xff0c;然后先从最后一列遍历&#xff0c;把一维数组里的元素&#xff0c;依次等于遍历的元素即可: void rotate(int** matrix, int matrixSize, int*…

RunMe_Aobut TC103848_UEFIShellFactoryDiagnostics.nsh

:: ***************************************************************************************************************************************************************** :: 20240617 :: 该脚本可以用于BIOS Case TC103848测试,功能包括&#xff1a;在EFIShell环境下运行…

Scala函数

文章目录 一、第1关&#xff1a;方法S 三角形 ​实验代码&#xff1a; 二、第2关&#xff1a;Scala函数以及函数调用实验代码&#xff1a; 一、第1关&#xff1a;方法 任务描述 本关任务&#xff1a;根据三角形的三边长 a、b、c&#xff0c;返回三角形的面积。 任意三角形面积…

外网怎么访问内网?

当我们需要在外网环境下访问内网资源时&#xff0c;常常会面临一些困扰。通过使用一些相关的技术与工具&#xff0c;我们可以轻松地实现这一目标。本文将介绍如何通过【天联】组网产品&#xff0c;解决外网访问内网的问题。 【天联】组网是一款由北京金万维科技有限公司自主研…

JAVAFX打包部署真正能用的办法(jdk21,javafx23)IntelliJ IDEA

我之前创建了javafx项目&#xff0c;想打包试试。一试&#xff0c;全是坑&#xff0c;所以记录下来&#xff0c;为有缘人节约时间。直接构建工件是错误的&#xff0c;别尝试了&#xff0c;找不在JDK的。我也花了一天多的时间尝试了网上各种大神的办法&#xff0c;就没找到一个是…

算法 Hw9

Hw 9 1 Scheduling with profits and deadlines12345 2 Parallel machine1234 1 Scheduling with profits and deadlines 1 决策问题表述&#xff1a; 给定一个利润值 P P P&#xff0c;是否存在一个任务调度方案使得完成所有任务的总利润至少为 P P P 2 在 NP 类中&…

stm32学习-软件I2C读取MPU6050

接线 SDAPB11SCLPB10 I2C 对操作端口的库函数进行封装 void MyI2C_W_SCL(uint8_t BitValue)//写 {GPIO_WriteBit(GPIOB, GPIO_Pin_10, (BitAction)BitValue);Delay_us(10); }void MyI2C_W_SDA(uint8_t BitValue)//写 {GPIO_WriteBit(GPIOB, GPIO_Pin_11, (BitAction)BitValu…

Redis 7.x 系列【4】命令手册

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Redis 版本 7.2.5 源码地址&#xff1a;https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 说明2. 命令手册2.1 Generic2.2 数据类型2.2.1 String2.2.2 Hash2.2.3 List2.2.4 S…

docker入门——镜像命令、容器命令及其他常用命令

镜像命令 dockers images [-a | -q] 查看镜像 -a: 显示所有细腻些 -q: 只显示镜像ID docker search [–filter]搜索命令 –filter字段值 docker pull 镜像名[:tag] 下载镜像不加tag默认下载最新镜像 docker rmi [options] 删除镜像 ​ -f 容器ID [,容器ID&#xff0c;容…

如何在纯内网环境下,将EasyCVR视频汇聚网关通过4G与第三方公网云平台级联?

EasyCVR视频汇聚网关是TSINGSEE青犀软硬一体的一款产品&#xff0c;可提供多协议的接入、音视频采集、处理&#xff0c;能实现海量前端设备的轻量化接入/转码/分发、视频直播、云端录像、云存储、检索回看、智能告警、平台级联等&#xff0c;兼容多种操作系统&#xff0c;轻松扩…

HTTP/2 协议学习

HTTP/2 协议介绍 ​ HTTP/2 &#xff08;原名HTTP/2.0&#xff09;即超文本传输协议 2.0&#xff0c;是下一代HTTP协议。是由互联网工程任务组&#xff08;IETF&#xff09;的Hypertext Transfer Protocol Bis (httpbis)工作小组进行开发。是自1999年http1.1发布后的首个更新。…

速盾:海外网站cdn加速免费

随着互联网的快速发展和全球化的趋势&#xff0c;海外网站的重要性也日益增加。然而&#xff0c;由于地理位置等各种因素的限制&#xff0c;海外访问海外网站的速度往往较慢&#xff0c;给用户的体验带来了不便。为了解决这个问题&#xff0c;许多网站开始采用CDN加速技术。 C…