C++学习笔记(十七)

模板

1. 模板的概念

模板就是建立通用的模具,大大提高复用性

模板的特点:

1. 模板不可以直接使用,它只是一个框架

2. 模板的通用并不是万能的

2. 函数模板

C++另一种编程思想称为泛型编程,主要利用的技术就是模板

C++提供两种模板机制:函数模板类模板

2.1 函数模板语法

函数模板作用:建立一个通用函数,其函数返回值类型和形参类型可以不具体制定,用一个虚拟的类型来代替

语法:

template<typename T>
函数声明或定义

解释:

template —— 声明创建模板

typename —— 表明其后面的符号是一种数据类型,可以用class代替

T —— 通用的数据类型,名称可以替换,通常为大写字母

#include <iostream>using namespace std;// 交换整型数据函数
void SwapInt(int& a, int& b)
{int temp = a;a = b;b = temp;
}// 交换浮点型数据函数
void SwapDouble(double& a, double& b)
{double temp = a;a = b;b = temp;
}// 利用模版提供通用的交换函数
template<typename T>
void mySwap(T& a, T& b)
{T temp = a;a = b;b = temp;
}void test()
{int a1 = 10;int b1 = 20;double c1 = 1.1;double d1 = 2.2;int a2 = 10;int b2 = 20;double c2 = 1.1;double d2 = 2.2;cout << "交换前:a1 = " << a1 << endl;cout << "交换前:b1 = " << b1 << endl;SwapInt(a1, b1);cout << "交换后:a1 = " << a1 << endl;cout << "交换后:b1 = " << b1 << endl;cout << "交换前:c1 = " << c1 << endl;cout << "交换前:d1 = " << d1 << endl;SwapDouble(c1, d1);cout << "交换后:c1 = " << c1 << endl;cout << "交换后:d1 = " << d1 << endl;cout << "交换前:a2 = " << a2 << endl;cout << "交换前:b2 = " << b2 << endl;// 1. 自动类型推导mySwap(a2, b2);cout << "交换后:a2 = " << a2 << endl;cout << "交换后:b2 = " << b2 << endl;cout << "交换前:c2 = " << c2 << endl;cout << "交换前:d2 = " << d2 << endl;// 2. 显示指定类型mySwap<double>(c2, d2);cout << "交换后:c2 = " << c2 << endl;cout << "交换后:d2 = " << d2 << endl;
}int main(int argc, char* argv[])
{test();return 0;
}

总结:

1. 函数模板利用关键字 template

2. 使用函数模板有两种方式:自动类型推导、显示指定类型

3. 模板的目的是为了提高复用性,将类型参数化

2.2 函数模板注意事项

注意事项:

1. 自动类型推导,必须推导出一致的数据类型T,才可以使用

2. 模板必须要确定出T的数据类型,才可以使用

2.3 函数模板案例

案例描述:

1. 利用函数模板封装一个排序的函数,可以对不同数据类型数据进行排序

2. 排序规则从小到大,排序算法为选择排序

3. 分别利用char数据和int数组进行测试

#include <iostream>using namespace std;// 交换函数模板
template<typename T>
void swapElement(T& a, T& b)
{T temp = a;a = b;b = temp;
}// 排序函数模板
template<typename T>
void arrSort(T arr[], int len)
{for (int i = 0; i < len - 1; i++){int min = i;for (int j = i + 1; j < len; j++){if (arr[min] > arr[j])min = j;}if (min != i){swapElement(arr[min], arr[i]);}}
}// 输出函数模板
template<typename T>
void arrPrint(T arr[], int len)
{for (int i = 0; i < len; i++){if (arr[i] != '\0'){cout << arr[i] << " ";}}cout << endl;
}// 测试char数组
void test01()
{char charArr[] = "cdefgab";int charLen = sizeof(charArr) / sizeof(char);arrSort(charArr, charLen);arrPrint(charArr, charLen);
}// 测试int数组
void test02()
{int intArr[] = { 6,9,3,0,8,4,1,7,5,2 };int charLen = sizeof(intArr) / sizeof(int);arrSort(intArr, charLen);arrPrint(intArr, charLen);
}int main(int argc, char* argv[])
{test01();test02();return 0;
}

2.4 普通函数和函数模板的区别

普通函数与函数模板区别:

1. 普通函数调用时可以发生自动类型转换(隐式类型转换)

2. 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换

3. 如果利用显示指定类型的方式,可以发生隐式类型转换

#include <iostream>using namespace std;// 普通函数
int myAdd01(int a, int b)
{return a + b;
}// 函数模板
template<typename T>
T myAdd02(T a, T b)
{return a + b;
}void test()
{int a = 10;char b = 'b';// 普通函数调用,输出108,将b转换为int类型,其值为b的ascll码值cout << "a + b = " << myAdd01(a, b) << endl; // 自动类型推导(报错)// cout << "a + b = " << myAdd02(a, b) << endl;// 显示指定类型:输出108cout << "a + b = " << myAdd02<int>(a, b) << endl;
}
int main(int argc, char* argv[])
{test();return 0;
}

总结:建议使用显示指定类型的方式,调用函数模板,因为可以自己确定通用类型T

2.5 普通函数与函数模板的调用规则

调用规则如下:

1. 如果函数模板和普通函数都可以调用,优先使用普通函数

2. 可以通过空模板参数列表来强制调用函数模板

3. 函数模板也可以发生重载

4. 如果函数模板可以产生更好的匹配,优先调用函数模板

#include <iostream>using namespace std;// 普通函数
void myPrint(int a, int b)
{cout << "调用的是普通函数" << endl;
}// 函数模板
template<typename T>
void myPrint(T a, T b)
{cout << "调用的是模板1" << endl;
}template<typename T>
void myPrint(T a, T b, T c)
{cout << "调用的是模板2" << endl;
}void test()
{int a = 10;int b = 20;// 1. 优先调用普通函数myPrint(a, b);// 2. 通过空模板参数列表,强制调用函数模板myPrint<>(a, b);// 3. 函数模板也可以发生重载myPrint(a, b, 100);// 4. 如果函数模板可以产生更好的匹配,优先调用函数模板char c1 = 'a';char c2 = 'b';// 模板1不用发生隐式类型转换,产生更好的匹配myPrint(c1, c2);
}int main(int argc, char* argv[])
{test();return 0;
}

总结:既然提供了函数模板,最好就不要提供普通函数,否则容易出现二义性

2.6 模板的局限性

局限性:模版的通用性并不是万能的

例如:

template<typename T>
void f(T a, T b)
{a = b;
}

在上述代码中提供的赋值操作,如果传入的a和b是一个数组,就无法实现了

例如:

template<typename T>
void func(T a, T b)
{if(a>b){...}
}

在上述代码中,如果T的数据类型传入的是想Person这样的自定义数据类型,也无法正常运行

因此C++为了解决这种问题,提供模板的重载,可以为这些特定的类型提供具体化的模板

#include <iostream>
#include <string>using namespace std;class Person
{
public:Person(string name, int age){this->m_Name = name;this->m_Age = age;}string m_Name;int m_Age;
};// 对比两个数据是否相等函数
template<typename T>
bool myCompare(T& a, T& b)
{if (a == b){return true;}else{return false;}
}void test01()
{int a = 10;int b = 20;bool res = myCompare(a, b);if (res == true){cout << "a == b" << endl;}else{cout << "a != b" << endl;}
}// 利用具体化Person的版本实现代码,具体化优先调用
template<> bool myCompare(Person& p1, Person& p2)
{if (p1.m_Name == p2.m_Name && p1.m_Age == p2.m_Age){return true;}else{return false;}
}void test02()
{Person p1("tom", 10);Person p2("tom", 10);bool res = myCompare(p1, p2);if (res == true){cout << "a == b" << endl;}else{cout << "a != b" << endl;}
}int main(int argc, char* argv[])
{test01();test02();return 0;
}

总结:

1. 利用具体化的模板,可以解决自定义类型的通用化

2. 学习模板并不是为了写模板,而是在STL中能够运用系统提供的模板

3. 类模板

3.1 类模板语法

类模板作用:建立一个通用类,类中的成员数据类型可以是不具体制定的,用一个虚拟的类型来代表

语法:

template<class T>
类

解释:

template —— 声明创建模板

class —— 表面其后面的符号是一种数据类型,可以用typename代替

T —— 通用的数据类型,名称可以替换,通常为大写字母

#include <iostream>
#include <string>using namespace std;template<class NameType, class AgeType>
class Person
{
public:NameType m_Name;AgeType m_Age;Person(NameType name, AgeType age){this->m_Name = name;this->m_Age = age;}void showPerson(){cout << "姓名:" << this->m_Name << endl;cout << "年龄:" << this->m_Age << endl;}
};void test()
{Person<string, int> p("tom", 10);p.showPerson();
}int main(int argc, char* argv[])
{test();return 0;
}

3.2 类模板和函数模板的区别

类模板与函数模板的区别主要有两点:

1. 类模板在模板参数列表中可以有默认参数

2. 类模板没有自动类型推导的使用方式

#include <iostream>
#include <string>using namespace std;// 1. 类模板在模板参数列表中可以有默认参数
template<class NameType, class AgeType = int>
class Person
{
public:NameType m_Name;AgeType m_Age;Person(NameType name, AgeType age){this->m_Name = name;this->m_Age = age;}void showPerson(){cout << "姓名:" << this->m_Name << endl;cout << "年龄:" << this->m_Age << endl;}
};void test()
{// 2. 类模板没有自动类型推导使用方式//Person p("tom", 10);Person<string>p("tom", 10);p.showPerson();
}int main(int argc, char* argv[])
{test();return 0;
}

3.3 类模板中成员函数创建时机

类模板中成员函数和普通类中成员函数创建时机是由区别的

普通类中的成员函数一开始就创建

类模板中的成员函数在调用时才创建

3.4 类模板对象做函数参数

类模板实例化出的对象,向函数传参的方式

一共有三种传入方式:

1. 指定传入的类型 — 直接显示对象的数据类型

2. 参数模板化        — 将对象中的参数变为模板进行传递

3. 整个类模板化     — 将这个对象类型模板化进行传递

#include <iostream>
#include <string>using namespace std;template<class NameType, class AgeType>
class Person
{
public:NameType m_Name;AgeType m_Age;Person(NameType name, AgeType age){this->m_Name = name;this->m_Age = age;}void showPerson(){cout << "姓名:" << this->m_Name << endl;cout << "年龄:" << this->m_Age << endl;}
};// 1. 指定传入类型
void printPerson01(Person<string, int>& p)
{p.showPerson();
}void test01()
{Person<string, int>p("孙悟空",100);printPerson01(p);
}// 2. 参数模板化
template<typename NameType, typename AgeType>
void printPerson02(Person<NameType, AgeType>& p)
{p.showPerson();// 查看推到出来的类型cout << "NameType的类型:" << typeid(NameType).name() << endl;cout << "AgeType的类型:" << typeid(AgeType).name() << endl;
}void test02()
{Person<string, int>p("猪八戒", 90);printPerson02(p);
}// 3. 整个类模板化
template<typename T>
void printPerson03(T& p)
{p.showPerson();// 查看推到出来的类型cout << "T的类型:" << typeid(T).name() << endl;
}void test03()
{Person<string, int>p("唐三藏", 45);printPerson03(p);
}int main(int argc, char* agrv[])
{test01();test02();test03();return 0;
}

总结:

1. 通过类模板创建的对象,可以有三种方式向函数中进行传参

2. 使用比较广泛的是第一种:指定传入的类型

3.5 类模板与继承

当类模板碰到继承时,需要注意以下几点:

1. 当子类继承的父类是一个类模板时,子类在声明的时候,需要制定出父类中T的类型

2. 如果不指定,编译器无法给子类分配内存

3. 如果想灵活指定出父类中T的类型,子类也需要变为类模板

#include <iostream>
#include <string>using namespace std;template<class T1>
class Base
{
public:T1 m_BaseNum;
};//class Son :public Base 错误,必须要知道父类中T1的类型,才可以继承给子类
class Son1 :public Base<int>{};// 如果想灵活指定父类中T1的类型,子类也需要变类模板
template<class T1, class T2>
class Son2 :public Base<T1>
{
public:T2 m_Son2Num;Son2(){cout << "Base中T1的数据类型为:" << typeid(T1).name() << endl;cout << "Son2中T2的数据类型为:" << typeid(T2).name() << endl;}
};
int main(int argc, char* argv[])
{Son2<string, int>s;return 0;
}

总结:如果父类是类模板,子类需要指定出父类中T的数据类型

3.6 类模板成员函数的类外实现

#include <iostream>
#include <string>using namespace std;template<class NameType, class AgeType>
class Person
{
public:NameType m_Name;AgeType m_Age;// 类内声明Person(NameType name, AgeType age);void showPerson();
};// 构造函数的类外实现
template<class NameType, class AgeType>
Person<NameType, AgeType>::Person(NameType name, AgeType age)
{this->m_Name = name;this->m_Age = age;
}// 成员函数的类外实现
template<class NameType, class AgeType>
void Person<NameType, AgeType>::showPerson()
{cout << "姓名:" << this->m_Name << endl;cout << "年龄:" << this->m_Age << endl;
}void test()
{Person<string, int>p("tom", 18);p.showPerson();
}int main(int argc, char* argv[])
{test();return 0;
}

总结:类模板中成员函数类外实现时,需要加上模板参数列表

3.7 类模板分文件编写

问题:类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到

解决:1. 直接包含.cpp源文件

           2. 将声明(.h)和实现(.cpp)写到同一文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制

1. 在头文件中创建XXX.h,在源文件中创建XXX.cpp

Person.h

#pragma once#include <iostream>
#include <string>using namespace std;template<class NameType, class AgeType>
class Person
{
public:NameType m_Name;AgeType m_Age;// 类内声明Person(NameType name, AgeType age);void showPerson();
};

Person.cpp

#include "Person.h"// 构造函数的类外实现
template<class NameType, class AgeType>
Person<NameType, AgeType>::Person(NameType name, AgeType age)
{this->m_Name = name;this->m_Age = age;
}// 成员函数的类外实现
template<class NameType, class AgeType>
void Person<NameType, AgeType>::showPerson()
{cout << "姓名:" << this->m_Name << endl;cout << "年龄:" << this->m_Age << endl;
}

main.cpp

#include <iostream>
#include <string>
#include "Person.cpp"using namespace std;void test()
{Person<string, int>p("tom", 18);p.showPerson();
}int main(int argc, char* argv[])
{test();return 0;
}

2. 在头文件中创建XXX.hpp

Person.hpp

#pragma once#include <iostream>
#include <string>using namespace std;template<class NameType, class AgeType>
class Person
{
public:NameType m_Name;AgeType m_Age;// 类内声明Person(NameType name, AgeType age);void showPerson();
};// 构造函数的类外实现
template<class NameType, class AgeType>
Person<NameType, AgeType>::Person(NameType name, AgeType age)
{this->m_Name = name;this->m_Age = age;
}// 成员函数的类外实现
template<class NameType, class AgeType>
void Person<NameType, AgeType>::showPerson()
{cout << "姓名:" << this->m_Name << endl;cout << "年龄:" << this->m_Age << endl;
}

main.cpp

#include <iostream>
#include <string>
#include "Person.hpp"using namespace std;void test()
{Person<string, int>p("tom", 18);p.showPerson();
}int main(int argc, char* argv[])
{test();return 0;
}

总结:主流的解决方式是第二种,将类模板成员函数写到一起,并将后缀名改为.hpp

3.8 类模板与友元

全局函数类内实现 — 直接在类内声明友元即可

全局函数类外实现 — 需要提前让编译器知道全局函数的存在

全局函数类内实现:

#include <iostream>
#include <string>using namespace std;template<class NameType, class AgeType>
class Person
{friend void showPerson(Person<NameType, AgeType> p){cout << "姓名:" << p.m_Name << endl;cout << "年龄:" << p.m_Age << endl;}
public:Person(NameType name, AgeType age){this->m_Name = name;this->m_Age = age;}
private:NameType m_Name;AgeType m_Age;
};void test()
{Person<string, int>p("tom", 18);showPerson(p);
}int main(int argc, char* argv[])
{test();return 0;
}

全局函数类外实现:

#include <iostream>
#include <string>using namespace std;// 提前让编译器知道Person类的存在,因为把全局函数的类外实现放在在前面
// 全局函数类外实现用到了Person
template<class NameType, class AgeType>
class Person;// 全局函数类外实现
template<class NameType, class AgeType>
void showPerson(Person<NameType, AgeType> p)
{cout << "姓名:" << p.m_Name << endl;cout << "年龄:" << p.m_Age << endl;
}template<class NameType, class AgeType>
class Person
{// 全局函数类内声明// 加空模板参数列表,因为类外实现变成了函数模板// 如果全局函数是类外实现,需要让编译器提前知道这个函数的存在friend void showPerson<>(Person<NameType, AgeType> p);public:Person(NameType name, AgeType age){this->m_Name = name;this->m_Age = age;}
private:NameType m_Name;AgeType m_Age;
};void test()
{Person<string, int>p("tom", 18);showPerson(p);
}int main(int argc, char* argv[])
{test();return 0;
}

总结:建议全局函数做类内实现,用法简单,而且编译器可以直接识别

3.9 类模板案例

案例分析:实现一个通用的数组类,需求如下:

1. 可以对内置数据类型以及自定义数据类型的数据进行存储

2. 将数组中的数据存储到堆区

3. 构造函数中可以传入数组的容量

4. 提供对应的拷贝构造函数以及operator=防止浅拷贝问题

5. 提供尾插法和尾删法对数组中的数据进行增加和删除

6. 可以通过下标的方式访问数组中的元素

7. 可以获取数组中当前元素的个数和数组的容量

MyArray.hpp

#pragma once#include <iostream>using namespace std;template<class T>
class MyArray
{
public:// 有参构造MyArray(int capacity){this->m_Capacity = capacity;this->m_Size = 0;this->pAddress = new T[this->m_Capacity];}// 拷贝构造函数MyArray(const MyArray& arr){this->m_Capacity = arr.m_Capacity;this->m_Size = arr.m_Size;// 深拷贝this->pAddress = new T[arr->pAddress];// 将arr中的数据都拷贝过来for(int i=0;i<this->m_Size;i++){this->pAddress[i] = arr.pAddress[i];}}// operator= 防止浅拷贝问题MyArray& operator=(const MyArray& arr){// 先判断原来堆区是否有数据,如果有先释放if (this->pAddress != NULL){delete[] this->pAddress;this->pAddress = NULL;this->m_Capacity = 0;this->m_Size = 0;}// 深拷贝this->m_Capacity = arr.m_Capacity;this->m_Size = arr.m_Size;this->pAddress = new T[arr->pAddress];// 将arr中的数据都拷贝过来for (int i = 0; i < this->m_Size; i++){this->pAddress[i] = arr.pAddress[i];}return *this;}// 尾插法void Push_Back(const T& val){// 判断容量是否等于大小if (this->m_Capacity == this->m_Size){return;}this->pAddress[this->m_Size] = val;this->m_Size++;}// 尾删法void Pop_Back(){// 让用户访问不到最后一个元素,即为尾删,逻辑删除if (this->m_Size == 0){return;}this->m_Size--;}// 通过下标的方式访问数组中的元素T& operator[](int index){return this->pAddress[index];}// 返回数组容量int getCapacity(){return this->m_Capacity;}//返回数组大小int getSize(){return this->m_Size;}// 析构函数~MyArray(){if (this->pAddress != NULL){delete[] this->pAddress;this->pAddress = NULL;}}
private:T* pAddress;  // 指针指向堆区开辟的真实数据int m_Capacity;  // 数组容量int m_Size;  // 数组大小
};

main.cpp

#include <iostream>
#include <string>
#include "MyArray.hpp"using namespace std;class Person
{
public:Person(){}Person(string name, int age){this->m_Name = name;this->m_Age = age;}string m_Name;int m_Age;
};void printIntArray(MyArray <int>& arr)
{for (int i = 0; i < arr.getSize(); i++){cout << arr[i] << " ";}cout << endl;
}void printPersonArray(MyArray<Person>& arr)
{for (int i = 0; i < arr.getSize(); i++){cout << "姓名:" << arr[i].m_Name << "\t年龄:" << arr[i].m_Age << endl;}
}// 测试内置数据类型
void test01()
{MyArray <int>arr1(20);for (int i = 0; i < 10; i++){// 利用尾插法向数组内插入数据arr1.Push_Back(i);}cout << "arr1的内容为:" << endl;printIntArray(arr1);cout << "arr1的大小为:" << arr1.getSize() << endl;cout << "arr1的容量为:" << arr1.getCapacity() << endl;// 利用尾删法删除数组尾部的数据arr1.Pop_Back();cout << "正在删除......" << endl;cout << "arr1的内容为:" << endl;printIntArray(arr1);cout << "arr1的大小为:" << arr1.getSize() << endl;cout << "arr1的容量为:" << arr1.getCapacity() << endl;
}// 测试自定义数据类型
void test02()
{MyArray<Person> arr(20);Person p1("韩信", 20);Person p2("赵云", 18);Person p3("刘邦", 30);Person p4("孙悟空", 99);Person p5("妲己", 15);Person p6("安琪拉", 13);// 将数据插入到数组中arr.Push_Back(p1);arr.Push_Back(p2);arr.Push_Back(p3);arr.Push_Back(p4);arr.Push_Back(p5);arr.Push_Back(p6);// 打印数组cout << "arr的内容为:" << endl;printPersonArray(arr);// 输出大小cout << "arr的大小为:" << arr.getSize() << endl;// 输出容量cout << "arr的容量为:" << arr.getCapacity() << endl;// 利用尾删法删除数组尾部的数据arr.Pop_Back();cout << "正在删除......" << endl;cout << "arr的内容为:" << endl;printPersonArray(arr);cout << "arr的大小为:" << arr.getSize() << endl;cout << "arr的容量为:" << arr.getCapacity() << endl;}int main(int argc, char* argv[])
{test01();test02();return 0;
}

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

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

相关文章

python本地缓存cacheout

cacheout地址&#xff1a; https://github.com/dgilland/cacheout 文档地址&#xff1a;https://cacheout.readthedocs.io 简单使用介绍 安装 pip install cacheout 使用 import timefrom cacheout import Cache# 默认的缓存(maxsize)大小为256&#xff0c;默认存活时间(t…

本地使用 docker 运行OpenSearch + Dashboard + IK 分词插件

准备基础镜像 注意一定要拉取和当前 IK 分词插件版本一致的 OpenSearch 镜像: https://github.com/aparo/opensearch-analysis-ik/releases 写这篇文章的时候 IK 最新版本 2.11.0, 而 dockerhub 上 OpenSearch 最新版是 2.11.1 如果版本不匹配的话是不能用的, 小版本号对不上…

11种方法判断​软件的安全可靠性​

软件的安全可靠性是衡量软件好坏的一个重要标准&#xff0c;安全性指与防止对程序及数据的非授权的故意或意外访问的能力有关的软件属性&#xff0c;可靠性指与在规定的一段时间和条件下&#xff0c;软件 软件的安全可靠性是衡量软件好坏的一个重要标准&#xff0c;安全性指与防…

通讯录应用程序开发指南

目录 一、前言 二、构建通讯录应用程序 2.1通讯录框架 (1)打印菜单 (2) 联系人信息的声明 (3)创建通讯录 (4)初始化通讯录 2.2功能实现 (1)增加联系人 (2)显示联系人 (3)删除联系人 (4)查找联系人 (5)修改联系人 (6)排序联系人 三、通讯录的优化 3.1 文件存储 …

Python办公自动化 – 语音识别和文本到语音的转换

Python办公自动化 – 对图片处理和文件的加密解密 以下是往期的文章目录&#xff0c;需要可以查看哦。 Python办公自动化 – Excel和Word的操作运用 Python办公自动化 – Python发送电子邮件和Outlook的集成 Python办公自动化 – 对PDF文档和PPT文档的处理 Python办公自动化 –…

PhysX——源码编译

从git下载源码 git主页 https://github.com/NVIDIA-Omniverse/PhysXclone地址 https://github.com/NVIDIA-Omniverse/PhysX.git源码编译 运行PhysX需要两个编译器的支持&#xff0c;CMake 3.12 或以上版本以及Python 2.7.6 版本 进入工程的 physx 目录&#xff0c;运行generate…

Java将枚举类转为json返回给前端

Java将枚举类转为json返回给前端 1.后端将枚举类直接返回给前端时只会显示枚举名称2.使用 JsonFormat 注解可以将枚举类转为json再返回给前端。2.1添加maven依赖2.2在枚举类上添加JsonFormat(shape JsonFormat.Shape.OBJECT)2.3编写接口返回给前端 1.后端将枚举类直接返回给前…

vue中的watcher

前言 Vue 中的 watcher 是一种特殊的对象&#xff0c;它可以订阅响应式数据的变化&#xff0c;并在变化时执行相应的回调函数。 Vue 中有三种类型的 watcher&#xff1a; 渲染 watcher 是负责更新视图的 watcher&#xff0c;每个组件实例都有一个对应的渲染 watcher。当组件实…

『Linux升级路』基础开发工具——gdb篇

&#x1f525;博客主页&#xff1a;小王又困了 &#x1f4da;系列专栏&#xff1a;Linux &#x1f31f;人之为学&#xff0c;不日近则日退 ❤️感谢大家点赞&#x1f44d;收藏⭐评论✍️ 目录 一、背景知识介绍 二、gdb指令介绍 一、背景知识介绍 在软件开发中&#xff0c…

WT2003HX高品质MP3语音芯片,支持立体声输出的应用优势

在当今的音频处理领域&#xff0c;立体声输出已经成为一项基本的需求。许多电子产品都要求能够支持立体声音效&#xff0c;为用户提供更为沉浸式的音频体验。针对这一市场需求&#xff0c;唯创知音推出了WT2003HX高品质MP3语音芯片&#xff0c;该芯片支持立体声输出&#xff0c…

Ubuntu 常用命令之 cal 命令用法介绍

&#x1f4d1;Linux/Ubuntu 常用命令归类整理 cal命令在Ubuntu系统下用于显示日历。它可以显示任何特定月份或整个年份的日历。 cal命令的参数如下 -1&#xff1a;只显示当前月份的日历。-3&#xff1a;显示前一个月、当前月和下一个月的日历。-s&#xff1a;指定日历的开始…

latex中工具包pdfcrop的使用:pdf自动裁剪

这里写自定义目录标题 https://blog.csdn.net/love_ljq/article/details/77604218 很多时候&#xff0c;我们在WORD或者是PPT里画完图之后&#xff0c;需要转换为矢量图&#xff0c;转换为矢量图有很多种方式&#xff0c;有一种方式就是转换为PDF文件&#xff0c;转换为PDF有一…

Redis命令参考手册完整版

Redis命令参考手册完整版 Key(键)DELKEYSRANDOMKEYTTLPTTLEXISTSMOVERENAMERENAMENXTYPEEXPIREPEXPIREEXPIREATPEXPIREATPERSISTSORT 获取 name 和 password<br />redis> SORT user_id BY user_level_* DESC GET user_name_* GET<br />user_password_*<br /&…

Linux chattr命令

Linux chattr命令用于改变文件属性。 这项指令可改变存放在ext2文件系统上的文件或目录属性&#xff0c;这些属性共有以下8种模式&#xff1a; a&#xff1a;让文件或目录仅供附加用途。 b&#xff1a;不更新文件或目录的最后存取时间。 c&#xff1a;将文件或目录压缩后存放…

ant-design-vue Message 用法以及内容为 html片段情况

全局配置&#xff1a; // main.ts// 进行全局配置 message.config({top: 0.7rem,//高度位置duration: 2,//提示持续时间maxCount: 1,//最大页面同时展示几条 });项目中最常用的用法&#xff1a; 1. 字符串 message.success("导入成功");2. html片段 message.error…

设计模式分类

不同设计模式的复杂程度、 细节层次以及在整个系统中的应用范围等方面各不相同。 我喜欢将其类比于道路的建造&#xff1a; 如果你希望让十字路口更加安全&#xff0c; 那么可以安装一些交通信号灯&#xff0c; 或者修建包含行人地下通道在内的多层互通式立交桥。 最基础的、 底…

音视频直播核心技术

直播流程 采集&#xff1a; 是视频直播开始的第一个环节&#xff0c;用户可以通过不同的终端采集视频&#xff0c;比如 iOS、Android、Mac、Windows 等。 前处理&#xff1a;主要就是美颜美型技术&#xff0c;以及还有加水印、模糊、去噪、滤镜等图像处理技术等等。 编码&#…

vue中监听Form表单值的变化

想要监听From表单中某个值的变化需要用到vue中的 watch watch: {inputForm.isHeating() {this.inputForm.otherHeating}}, isHeating是表单中的某个值&#xff0c;如果他变化就会清空另一个值

泛微OA C# 调用 WebAPI功能实现

泛微OA C# 调用 WebAPI功能实现 OA 在线文档地址1. 创建流程字段参数 mainData 简单说明字段表明细表2. 接口封装2.1 接口初始化2.2 接口注册2.3 获取Token2.4 拼装 Headers2.5 常用工作流方法2.5.1 创建2.5.2 删除2.5.3 撤回2.5.4 退回3. 接口调用OA 在线文档地址 Token认证 …

12.21_黑马数据结构与算法笔记Java

//最近在复习&#xff0c;&#xff0c;java的进度会比较慢一些 目录 219 排序算法 基数排序2 220 排序算法 java排序 221 排序 e01 根据另一个数组次序排序 222 排序 e02 根据出现频率排序 thinking&#xff1a;关于比较器 223 排序 e03 最大间距 解法1&#xff08;超出内…