【C++篇】引领C++模板初体验:泛型编程的力量与妙用
文章目录
- C++模板编程
- 前言
- 第一章: 初始模板与函数模版
- 1.1 什么是泛型编程?
- 1.1.1 为什么要有泛型编程?
- 1.1.1 泛型编程的优势
- 1.2 函数模板的基础
- 1.2.1 什么是函数模板?
- 1.2.2 函数模板的定义格式
- 1.2.3 示例:通用的交换函数
- 输出示例:
- 1.2.4 模板中的`typename`与`class`
- 1.3 函数模板的原理
- 1.3.1 函数模板的实例化
- 1.3.2 隐式实例化与显式实例化
- 第二章: 类模板
- 2.1 类模板概念
- 2.1.1 类模板的定义格式
- 2.1.2 示例:简单的类模板
- 输出示例:
- 2.2 类模板的实例化
- 2.3 类模板中的成员函数定义
- 2.4 为什么不建议类模板的定义和声明分离?
- 2.4.1 模板的编译时行为
- 2.4.2 链接器无法找到定义
- 2.4.3 无法预编译模板
- 2.4.4 解决方案:将声明和定义放在同一个头文件中
- 第三章: 模板的匹配原则
- 3.1 模板的匹配原则
- 写在最后
C++模板编程
💬 欢迎讨论:如果你在学习过程中有任何问题或想法,欢迎在评论区留言,我们一起交流学习。你的支持是我继续创作的动力!
👍 点赞、收藏与分享:觉得这篇文章对你有帮助吗?别忘了点赞、收藏并分享给更多的小伙伴哦!你们的支持是我不断进步的动力!
🚀 分享给更多人:如果你觉得这篇文章对你有帮助,欢迎分享给更多对C++感兴趣的朋友,让我们一起进步!
前言
C++作为一门强大的编程语言,以其丰富的功能和灵活的设计著称。模板编程是C++中非常重要的一个特性,通过模板可以实现泛型编程,编写与数据类型无关的代码,极大地提高了代码的复用性和可维护性。本文将从泛型编程、函数模板、类模板等几个方面详细讲解C++模板的使用,并结合实际的代码示例进行分析,帮助大家全面掌握模板编程的知识。
本篇文章将包含以下几个部分:
- 泛型编程的基本概念
- 函数模板的定义与使用
- 类模板的实现
- 模板的匹配原则
通过阅读本文,你将能够掌握C++模板编程的基础知识,理解其背后的工作原理,并学会如何在实际项目中应用这些技术。
第一章: 初始模板与函数模版
1.1 什么是泛型编程?
泛型编程(Generic Programming)是C++中的一种编程范式,旨在编写与数据类型无关的通用代码。这意味着你可以编写一次代码,并通过不同的数据类型进行复用。C++通过模板(Template)来实现泛型编程,模板是泛型编程的核心工具。
1.1.1 为什么要有泛型编程?
- 问题提出:如何实现一个通用的交换函数呢?
void Swap(int& left, int& right)
{int temp = left;left = right;right = temp;
}
void Swap(double& left, double& right)
{double temp = left;left = right;right = temp;
}
void Swap(char& left, char& right)
{char temp = left;left = right;right = temp;
}
//....等等
使用函数重载虽然可以实现,但是有一下几个不好的地方:
- 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数
- 代码的可维护性比较低,一个出错可能所有的重载均出错
那能否告诉编译器一个模子,让编译器根据不同的类型利用该模子来生成代码呢?
如果在C++中,也能够存在这样一个模具,通过给这个模具中填充不同材料(类型),来获得不同
材料的铸件(即生成具体类型的代码),那将会节省许多头发。巧的是前人早已将树栽好,我们只需在此乘凉。这就是泛型编程
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。
1.1.1 泛型编程的优势
泛型编程的主要优势包括:
- 代码复用性强:通过模板,你可以避免为每个数据类型单独编写相同功能的代码。
- 提高代码的可维护性:代码只需编写一次,减少了冗余代码,后续如果需要修改或修复,只需在一处进行。
- 减少编写错误:重复编写代码时容易出错,而模板可以让编译器自动生成所需代码,减少人为失误。
1.2 函数模板的基础
1.2.1 什么是函数模板?
函数模板(Function Template)是一个与类型无关的函数“蓝图”。通过模板参数,编译器在编译期间会根据实际的数据类型生成相应的函数版本。
1.2.2 函数模板的定义格式
我们可以通过以下格式来定义一个函数模板:
template<typename T>
返回类型 函数名(参数列表) {// 函数体
}
template
:告诉编译器接下来的内容是模板。typename T
:定义一个模板参数T
,可以用来表示任何类型。返回类型
和参数列表
可以使用T
作为数据类型。
注意:typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替
class),下文会讲到
1.2.3 示例:通用的交换函数
那我们就可以使用模板来编写交换函数,它可以交换任意类型的数据:
/*** @brief 通用的交换函数* @tparam T 通用的类型参数,由编译器根据实参推断* @param left 左侧变量* @param right 右侧变量*/
template<typename T>
void Swap(T& left, T& right) {T temp = left;left = right;right = temp;
}int main() {int a = 10, b = 20;double x = 1.1, y = 2.2;char c1 = 'A', c2 = 'B';// 使用模板函数进行交换Swap(a, b); Swap(x, y); Swap(c1, c2); std::cout << "交换后的整数: " << a << " " << b << std::endl;std::cout << "交换后的浮点数: " << x << " " << y << std::endl;std::cout << "交换后的字符: " << c1 << " " << c2 << std::endl;return 0;
}
输出示例:
交换后的整数: 20 10
交换后的浮点数: 2.2 1.1
交换后的字符: B A
1.2.4 模板中的typename
与class
在定义模板时,typename
和class
是可以互换的。你可以选择以下两种方式:
template<typename T> // 使用 typename
template<class T> // 使用 class
虽然两者功能相同,但推荐使用typename
,因为它能够更好地表达该参数是一个类型参数,避免与类的定义产生混淆。
1.3 函数模板的原理
函数模板的核心在于它不是一个真正的函数,而是一个编译器用来生成特定类型函数的蓝图。编译器根据模板的使用情况推导出具体的类型,并生成相应的代码。这一过程称为模板的实例化。
1.3.1 函数模板的实例化
当我们调用模板函数时,编译器会根据实际的参数类型生成对应的函数版本。比如:
template<class T>
T Add(const T& left, const T& right) {return left + right;
}int main() {int a = 10, b = 20;double x = 1.1, y = 2.2;Add(a, b); // 生成 Add<int> 版本Add(x, y); // 生成 Add<double> 版本return 0;
}
在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应
类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,
将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此。
1.3.2 隐式实例化与显式实例化
template<class T>T Add(const T& left, const T& right)
{return left + right;
}
int main()
{int a1 = 10, a2 = 20;double d1 = 10.0, d2 = 20.0;Add(a1, a2);Add(d1, d2);Add(a1, d1);/*
该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型
通过实参a1将T推演为int,通过实参d1将T推演为double类型,但模板参数列表中只有
一个T,
编译器无法确定此处到底该将T确定为int 或者 double类型而报错
注意:在模板中,编译器一般不会进行类型转换操作,因为一旦转化出问题,编译器就需要
背黑锅*/// 此时有两种处理方式:1. 用户自己来强制转化 2. 使用显式实例化Add(a1, (int)d1);//自己强制转换return 0;
}
模板实例化分为两种:
- 隐式实例化:编译器根据实参推导出模板参数,并自动生成函数。例如上面的
Add
函数就是隐式实例化。 - 显式实例化:如果想要强制指定模板参数,可以使用显式实例化:
就是第二种处理方式
int main(void)
{int a = 10;double b = 20.0;// 显式实例化Add<int>(a, b);return 0;
};
第二章: 类模板
2.1 类模板概念
类模板(Class Template)是用于定义与类型无关的类,它允许我们在类的定义中使用模板参数,编译时再根据实际类型进行类的实例化。类模板与函数模板类似,只不过它是用来生成类的。
2.1.1 类模板的定义格式
定义类模板的语法格式如下:
template<class T>
class 类名 {// 类的成员变量和方法
};
template<class T>
:告诉编译器这是一个模板类,T
是一个类型参数。- 类的成员和方法可以使用
T
作为数据类型,编译时由用户提供的类型来替代T
。
2.1.2 示例:简单的类模板
下面是一个简单的栈(Stack)类模板,用于存储任意类型的数据:
#include<iostream>using namespace std;/*** @brief 栈的类模板* @tparam T 通用的类型参数*/
template<class T>
class Stack {
public:Stack(size_t capacity = 4) {_array = new T[capacity];_capacity = capacity;_size = 0;}~Stack() {delete[] _array;}/// @brief 将元素压入栈中void Push(const T& data) {if (_size == _capacity) {Expand(); // 扩容}_array[_size++] = data;}/// @brief 弹出栈顶元素void Pop() {if (_size > 0) {--_size;}}/// @brief 返回栈顶元素T& Top() {if (_size > 0) {return _array[_size - 1];}throw out_of_range("栈为空!");}/// @brief 检查栈是否为空bool IsEmpty() const {return _size == 0;}/// @brief 获取栈中元素的数量size_t Size() const {return _size;}private:T* _array;size_t _capacity;size_t _size;/// @brief 扩展栈的容量void Expand() {size_t newCapacity = _capacity * 2;T* newArray = new T[newCapacity];for (size_t i = 0; i < _size; ++i) {newArray[i] = _array[i];}delete[] _array;_array = newArray;_capacity = newCapacity;}
};int main() {Stack<int> st; // 创建存储整数的栈st.Push(10);st.Push(20);st.Push(30);cout << "栈顶元素: " << st.Top() << endl;st.Pop();cout << "弹出后栈顶元素: " << st.Top() << endl;return 0;
}
输出示例:
栈顶元素: 30
弹出后栈顶元素: 20
在这个类模板中,T
是一个通用类型参数。Stack<int>
是Stack
类模板的一个实例化,表示它是一个存储int
类型数据的栈。编译器会根据实际使用的类型自动生成相应的类。
2.2 类模板的实例化
与函数模板不同,类模板在使用时必须显示地提供类型参数。实例化类模板时,必须在类名后面的尖括号<>
中指定实际的类型参数。例如:
Stack<int> st1; // 实例化为处理 int 类型的栈
Stack<double> st2; // 实例化为处理 double 类型的栈
这里的Stack<int>
和Stack<double>
分别表示不同的类型,即不同的模板实例。编译器会根据模板参数生成相应的类代码。
2.3 类模板中的成员函数定义
对于类模板,成员函数可以在类定义内或定义外实现。类模板的成员函数定义外置时,需要在函数名之前加上模板声明和模板参数。例如:
template<class T>
void Stack<T>::Push(const T& data) {if (_size == _capacity) {Expand(); // 扩容}_array[_size++] = data;
}
2.4 为什么不建议类模板的定义和声明分离?
在C++中,类模板的实现与普通类有一个显著的区别:模板是在编译时根据实际类型实例化的,而不是像普通的类那样在编译期和链接期处理。这导致了一个很重要的问题:如果将类模板的声明和定义分离到不同的文件中,可能会导致链接错误。以下是详细原因:
2.4.1 模板的编译时行为
类模板的本质是一个“蓝图”,它并不是一个完整的类,而是一个在需要时根据实际类型生成代码的模式。因此,模板只有在实际使用(实例化)时,编译器才会生成对应的类型的代码。编译器无法预先知道你会使用哪些类型来实例化模板,因此它不会为模板生成实际的代码。
2.4.2 链接器无法找到定义
当你将类模板的声明放在头文件中,而把定义放在.cpp
文件中时,模板实例化的过程可能发生在不同的编译单元中。因为模板只有在编译期被实例化,链接器在链接时无法看到模板的定义,除非在编译时所有模板的实例化代码都可见。如果定义在.cpp
文件中,其他使用模板的编译单元无法找到这个定义,导致链接器报错。
2.4.3 无法预编译模板
与普通类不同,类模板无法被预编译或只在一个编译单元中定义然后供其他单元使用。普通的类在编译过程中,编译器会生成目标代码并储存在.obj
文件中,链接时其他编译单元可以引用这些已生成的代码。而类模板无法这样做,因为它需要知道使用时的类型才能生成实际的代码。
2.4.4 解决方案:将声明和定义放在同一个头文件中
为了避免上述问题,C++的惯用方法是将类模板的声明和定义都放在同一个头文件中。这使得每个使用模板的编译单元在实例化模板时,编译器能够访问到模板的定义,并根据需要生成实际的代码。这种方式确保了编译器能够在编译期处理模板的实例化,而不会在链接时出现找不到定义的问题。
错误用法:
// Stack.h
template<typename T>
class Stack {
public:void Push(const T& value);// 声明在头文件中
};// Stack.cpp
template<typename T>
void Stack<T>::Push(const T& value) {// 定义在.cpp文件中
}
在这种情况下,如果不同编译单元使用了Stack<int>
或Stack<double>
,链接器可能会报错,因为它无法找到模板的定义。
正确用法:
// Stack.h
template<typename T>
class Stack {
public:void Push(const T& value);
};template<typename T>
void Stack<T>::Push(const T& value) {// 声明和定义都在头文件中
}
这种方法确保每个编译单元都能访问到模板的完整定义,避免链接时的错误。
总结:
类模板的代码只有在实例化时才生成,因此类模板的定义必须在每个使用它的编译单元中可见。将模板的声明和定义放在同一个头文件中,可以确保模板实例化时能够访问到其定义,避免链接错误。这也是为什么大多数C++开发者在编写模板时会将模板的实现放在头文件中的原因。
注意:
函数模板与类模板不同,当代大多数编译器支持函数模板的声明和定义分离,这是因为函数模板的实例化往往只涉及函数的具体调用,不像类模板这么复杂,具体之后的博客会更详细的讲解此处的内容,敬请期待哦💕
第三章: 模板的匹配原则
3.1 模板的匹配原则
C++编译器在调用模板时,会根据实参类型和函数参数类型进行匹配。模板的匹配规则如下:
-
优先调用非模板函数:如果存在一个与实参完全匹配的非模板函数,编译器将优先调用非模板函数,而不是通过模板生成一个实例。
例如:
int Add(int a, int b) {return a + b; }template<typename T> T Add(T a, T b) {return a + b; }int main() {int a = 10, b = 20;cout << Add(a, b) << endl; // 调用非模板版本 }
-
如果非模板函数没有匹配,则调用模板实例:如果模板函数比非模板函数更能匹配参数类型,编译器将生成模板实例。
例如:
double Add(double a, double b) {return a + b; }template<typename T> T Add(T a, T b) {return a + b; }int main() {int a = 10, b = 20;double x = 1.1, y = 2.2;cout << Add(a, b) << endl; // 调用模板实例 Add<int>cout << Add(x, y) << endl; // 调用非模板版本 Add(double, double) }
写在最后
本文基础的讲解了C++模板编程的基础知识,涵盖了泛型编程、函数模板、类模板、模板匹配原则等概念。通过这些模板功能,C++开发者可以编写更加灵活和可复用的代码,大幅提高编程效率。
模板编程虽然强大,但使用时也需要谨慎,尤其是在处理模板特化和匹配规则时。如果能够合理地使用模板技术,相信你的代码质量将会有显著提升。
以上就是关于【C++篇】引领C++模板初体验:泛型编程的力量与妙用的内容啦,在之后会有另一篇博客来讲解有关模板的更多进阶内容,敬请期待哦,然后各位大佬有什么问题欢迎在评论区指正,或者私信我也是可以的啦,您的支持是我创作的最大动力!❤️
相关文章:

【C++篇】引领C++模板初体验:泛型编程的力量与妙用
文章目录 C模板编程前言第一章: 初始模板与函数模版1.1 什么是泛型编程?1.1.1 为什么要有泛型编程?1.1.1 泛型编程的优势 1.2 函数模板的基础1.2.1 什么是函数模板?1.2.2 函数模板的定义格式1.2.3 示例:通用的交换函数输出示例&am…...

在react中 使用redux
1.安装redux npm install reduxjs/toolkit react-redux 2.创建切片模块化数据 在Src目录下创建store目录,创建moude目录 创建tab.js import { createSlice } from reduxjs/toolkit; const tabSlice createSlice({name: tab,initialState: {Collapse: false,},re…...

计算机毕业设计python+spark知识图谱房价预测系统 房源推荐系统 房源数据分析 房源可视化 房源大数据大屏 大数据毕业设计 机器学习
《PythonSpark知识图谱房价预测系统》开题报告 一、研究背景与意义 随着城市化进程的加速和房地产市场的不断发展,房价成为影响人们生活质量的重要因素之一。准确预测房价不仅有助于政府制定科学的房地产政策,还能为开发商提供市场参考,同时…...

Spring-bean的生命周期-终篇
阶段8:Bean属性设置阶段 属性设置阶段分为3个小的阶段 实例化后阶段Bean属性赋值前处理Bean属性赋值 实例化后阶段 这里也有spring给我们预留了扩展,就是实现InstantiationAwareBeanPostProcessor的postProcessAfterInstantiation方法,开发…...

Kotlin 枚举和 when 表达式(六)
导读大纲 1.1 表示和处理选择: Enums和when1.1.1 声明枚举类和枚举常量1.1.2 使用 when 表达式处理枚举类 1.1 表示和处理选择: Enums和when 在本节中,我们将以在 Kotlin 中声明枚举为例,介绍 when 结构 when可以被视为比 Java 中 switch 结构更强大、更常用的替代品 1.1.1 …...

数字范围按位与
优质博文:IT-BLOG-CN 题目 给你两个整数left和right,表示区间[left, right],返回此区间内所有数字 按位与 的结果(包含left、right端点)。 示例 1: 输入:left 5, right 7 输出:…...

WebRTC编译后替换libwebrtc.aar时提示找不到libjingle_peerconnection_so.so库
Loading native library: jingle_peerconnection_so 问题原因:编译的时候只编译了armeabi-v7a的版本,但是应用程序是arm64-v8a,所以无法运行 解决方法:更新编译脚本,加上arm64-v8a进行编译 ./tools_webrtc/android/bu…...

Nature Electronics |无感佩戴的纤维基电子皮肤(柔性半导体器件/柔性健康监测/电子皮肤/柔性传感/纤维器件)
英国剑桥大学Yan Yan Shery Huang课题组,在《Nature Electronics 》上发布了一篇题为“Imperceptible augmentation of living systems with organic bioelectronic fibres”的论文,第一作者为王文宇博士(Wenyu Wang),论文内容如下: 一、 摘要 利用电子技术对人类皮肤和…...

深入剖析Docker容器安全:挑战与应对策略
随着容器技术的广泛应用,Docker已成为现代应用开发和部署的核心工具。它通过轻量级虚拟化技术实现应用的隔离与封装,提高了资源利用率。然而,随着Docker的流行,其安全问题也成为关注焦点。容器化技术虽然提供了良好的资源隔离&…...

后端技术打怪升级之路
记录后端技术打怪升级之路,如下是个人总记的主要技术栈,仅供参考! 备注: 同名文章一同步发表于个人网站及微信公众号 个人网站 工藤新一的技术小窝...

Leetcode 3296. Minimum Number of Seconds to Make Mountain Height Zero
Leetcode 3296. Minimum Number of Seconds to Make Mountain Height Zero 1. 解题思路2. 代码实现 题目链接:3296. Minimum Number of Seconds to Make Mountain Height Zero 1. 解题思路 这一题的思路的话我们采用的是一个二分法的思路,找到一个最大…...

计算机毕业设计之:基于深度学习的路面检测系统(源码+部署文档+讲解)
博主介绍: ✌我是阿龙,一名专注于Java技术领域的程序员,全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师,我在计算机毕业设计开发方面积累了丰富的经验。同时,我也是掘金、华为云、阿里云、InfoQ等平台…...

测试面试题:接口自动化测试流程?
1、测试用例编写:根据接口的需求和功能,编写相应的测试用例。测试用例应包括正常、边界和异常等各种情况下的测试。 2、准备测试数据:根据测试用例的要求,准备相应的测试数据。数据可以通过手动输入、数据库查询、文件导入等方式进…...

Golang面试题
在Golang(也称为Go语言)工程师的面试中,可能会遇到各种技术性和概念性的问题。 一、基础部分 Golang 中 make 和 new 的区别? 共同点:两者都用于分配内存。不同点: make 专为 slice、map 和 channel 设计,返回初始化后的(非零)值。new 分配内存并返回指向该内存的指针…...

《飞机大战游戏》实训项目(Java GUI实现)(设计模式)(简易)
目录 一、最终实现后,效果如下。 (1)简单介绍本游戏项目(待完善) (2)运行效果图(具体大家自己可以试) 初始运行情况。 手动更换背景图。 通过子弹攻击敌机,累…...

计算机毕业设计 基于 Hadoop平台的岗位推荐系统 SpringBoot+Vue 前后端分离 附源码 讲解 文档
🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点…...

【数据结构与算法】LeetCode:二分查找
文章目录 二分查找二分查找搜索插入位置 (Hot 100)x 的平方根搜索二维矩阵(Hot 100)在排序数组中查找元素的第一个和最后一个位置 (Hot 100)搜索旋转排序数组 (Hot 100)寻找旋转排序…...

专题·大模型安全 | 生成式人工智能的内容安全风险与应对策略
正如一枚硬币的两面,生成式人工智能大模型(以下简称“生成式大模型”)在助力内容生成的同时也潜藏风险,成为虚假信息传播、数据隐私泄露等问题的温床,加剧了认知域风险。与传统人工智能(AI)相比…...

CORS跨域+Nginx配置、Apache配置
CORS(Cross-Origin Resource Sharing,跨源资源共享)是一种机制,它使用额外的HTTP头部来告诉浏览器允许一个网页运行的脚本从不同于它自身来源的服务器上请求资源(例如字体、JavaScript、CSS等)。这是一种安…...

文件查找和打包压缩【1.7】
文件查找和打包压缩【1.7】 八、文件查找和打包压缩8.1 文件查找8.1.1 locate8.1.2 findfind8.1.2.1 指定搜索目录层级8.1.2.2 先处理文件再处理目录8.1.2.3 根据文件名和inode查找8.1.2.4 根据属主属组查找8.1.2.5 根据文件类型查找8.1.2.6 空文件或目录8.1.2.7 组合条件8.1.2…...

速盾:cdn一般多长时间清理下缓存?
CDN(Content Delivery Network)是一种网络加速技术,通过将网站的静态资源(如图片、视频、CSS、JavaScript等)分布到全球各地的服务器节点上,从而提高用户访问这些资源的速度和体验。CDN还具备缓存功能&…...

react hooks--useRef
基本用法 在类组件中获取一个dom元素实例,可以通过React.CreateRef或者回调函数的方式去获取。语法:const refContainer useRef(initialValue);使用场景:在 React 中进行 DOM 操作时,用来获取 DOM作用:返回一个带有 …...

GPT对话知识库——将寄存器中的一位数据读到变量中需要什么步骤?C语言中掩码的作用。
目录 1,问: 1,答: 1. 确定目标寄存器地址 2. 定位目标位 位操作的基本步骤: 3. 示例代码 示例步骤: 4. 详细解释步骤 5. 举例 6. 常见用法 总结 注: C语言中掩码的作用:…...

【计算机网络】运输层协议解析
前言 运输层直接为应用进程间的逻辑通信提供服务。运输层向高层用户屏蔽了下面网络核心细节(如网络拓扑、路由选择协议等)它使应用进程看见的就好像是在两个运输层实体之间有一条端到端的逻辑通信信道。 UDP与TCP对比 UDP: 无连接 支持一对…...

Redis存储原理
前言 我们从redis服务谈起,redis是单reactor,命令在redis-server线程处理。还有若干读写IO线程负责IO操作(redis6.0之后,Redis之pipeline与事务)。此外还有一个内存池线程负责内存管理、一个后台文件线程负责大文件的关…...

PHP、Java等其他语言转Go时选择GoFly快速快速开发框架指南
概要 经过一年多的发展GoFly快速开发框架已被一千多家科技企业或开发者用于项目开发,它的简单易学得到其他语言转Go首选框架。且企业版的发展为GoFly社区提供资金,这使得GoFly快速框架得到良好的发展,GoFly技术团队加大投入反哺科技企业和开…...

【MySQL】获取最近7天和最近14天的订单数量,使用MySQL详细写出,使用不同的方法
1. 获取最近7天和最近14天的订单数量,使用MySQL详细写出,使用不同的方法 要获取最近7天和最近14天的订单数量,我们可以使用不同的方法来优化查询性能。以下是两种方法: 1.1 方法一:使用日期计算 SELECTSUM(CASE WHE…...

WebView2新增、修改、删除、禁用右键菜单相关操作。
参考链接:WebView2操作右键菜单...

使用vue创建项目
一、安装环境 二、创建vue框架(创建文件夹,摁shift鼠标右键 打开) 1、项目配置 2、新增目录 三、路径别名配置 输入/ ,VSCode会联想出src下的所有子目录和文件,统一文件路径访问时不容易出错 四、ElementPlus配置 1、组件分为…...

Apache CVE-2021-41773 漏洞攻略
漏洞简介 该漏洞是由于Apache HTTP Server 2.4.49版本存在⽬录穿越漏洞,在路径穿越⽬录 <Directory/>Require all granted</Directory>允许被访问的的情况下(默认开启),攻击者可利⽤该路径穿越漏洞读取到Web⽬录之外的其他⽂件在…...