【C++入门到精通】C++入门 —— 模版(template)
阅读导航
- 前言
- 一、模版的概念
- 二、函数模版
- 1. 函数模板概念
- 2. 函数模板定义格式
- 3. 函数模板的原理
- 4. 函数模版的实例化
- 🚩隐式实例化
- 🚩显式实例化
- 5. 函数模板的匹配原则
- 三、类模板
- 1. 类模板的定义格式
- 2. 类模板的实例化
- 四、非类型模板参数
- 1. 概念
- 2. 定义
- 五、模板的特化
- 1. 概念
- 2. 函数模版特化
- 3. 类模版特化
- ⭕全特化
- ⭕偏特化
- 4. 模版特化应用示例
- 六、模板分离编译
- 1. 什么是分离编译
- 2. 模版的分离编译
- 七、模版的优缺点
- 【优点】
- 【缺点】
- 温馨提示
前言
前面我们讲了C语言的基础知识,也了解了一些数据结构,并且讲了有关C++的命名空间的一些知识点以及关于C++的缺省参数、函数重载,引用 和 内联函数也认识了什么是类和对象以及怎么去new一个 ‘对象’ ,以及学习了几个STL的结构也相信大家都掌握的不错,接下来博主将会带领大家继续学习有关C++比较重要的知识点—— 模版(template)。下面话不多说坐稳扶好咱们要开车了😍
一、模版的概念
模板是C++中的一种编程工具,它允许使用通用代码来定义函数和类,以适应多种类型或值的需求,从而实现代码的复用和泛化。模板实质上是一种参数化的类型或值的规范。通过模板的使用,可以提高代码的复用性和拓展性,使得代码更加通用并能适应不同类型或值的需求。模板可以在编译时生成针对不同类型或值的代码,从而提高代码的效率和灵活性。在C++中,有两种类型的模板:函数模板和类模板。下面博主来逐个介绍。
二、函数模版
1. 函数模板概念
函数模板允许定义一个通用的函数
,其中一些或全部的参数的类型可以是参数化的。使用函数模板时,编译器根据实际使用的参数类型,自动生成对应的函数代码。
2. 函数模板定义格式
template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表)
{
}
🚨注意:typename
是用来定义模板参数关键字,也可以使用class
(切记:不能使用struct
代替class
)
使用模版定义一个交换函数
template<typename T>
void Swap( T& left, T& right)
{T temp = left;left = right;right = temp;
}
使用模版定义一个取较大值函数
template <typename T>
T getMax(T a, T b) {return a > b ? a : b;
}int main() {int maxInt = getMax(2, 5); // 使用函数模板实例化为 int 类型的函数double maxDouble = getMax(3.14, 2.5); // 使用函数模板实例化为 double 类型的函数// ...
}
3. 函数模板的原理
函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器。
在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double类型,然后产生一份专门处理double类型的代码,对于字符类型也是如此。
4. 函数模版的实例化
用不同类型的参数使用函数模板时,称为函数模板的实例化。函数模板的实例化过程可以分为两个步骤:模板参数推断和模板函数生成。
-
模板参数推断:编译器根据实际传入的参数类型推导出模板参数的具体类型。编译器会尝试根据传递的参数类型来匹配模板参数,并确定参数的具体类型。如果无法进行准确的匹配,则可能会产生模板参数推断失败的错误。
-
模板函数生成:根据推断出的模板参数类型,编译器生成特定类型的函数代码。编译器使用推断出的参数类型来替换函数模板中的模板参数,生成与传递的参数类型匹配的函数定义。
⭕函数模板的实例化是在编译时完成的,它提前为不同的参数类型生成了不同的函数定义,以提高代码的重用性和执行效率。
⭕模板参数实例化分为:隐式实例化和显式实例化
🚩隐式实例化
下面是一个示例,展示函数模板隐式实例化的过程:
template <typename T>
T getMax(T a, T b) {return a > b ? a : b;
}int main() {int maxInt = getMax(2, 5); // 实例化为 int 类型的函数,参数推断为 intdouble maxDouble = getMax(3.14, 2.5); // 实例化为 double 类型的函数,参数推断为 doublechar maxChar = getMax('a', 'b'); // 实例化为 char 类型的函数,参数推断为 char// ...
}
在这个例子中,编译器会根据传递的参数类型自动推断模板参数的类型,并生成对应类型的函数代码。实例化后会生成 getMax
函数的具体定义,其中的模板参数 T
被替换为相应的类型。
🚩显式实例化
模板的显式实例化是指在编译时明确告诉编译器需要实例化的模板类型,以生成对应的函数定义。
在函数名后的<>中指定模板参数的实际类型
下面是一个示例,展示函数模板显式实例化的过程:
template <typename T>
T getMax(T a, T b) {return a > b ? a : b;
}
int main() {int maxInt = getMax<int>(2, 5); // 显式实例化的 int 类型的函数int maxDouble = getMax<double>(1.1, 5.2)// 显式实例化的 double类型的函数// ...
}
通过显式实例化,可以在编译时生成特定类型的函数定义,避免了模板参数推断和函数生成的开销,提高了代码的执行效率。
5. 函数模板的匹配原则
-
最佳匹配原则:编译器会尝试找到与调用参数最匹配的函数模板来实例化。在函数模板的候选函数中,编译器会根据实际参数类型进行以下规则的匹配:
a. 完全匹配:如果有一个函数模板能够完全匹配实际参数的类型,那么它将被选择为最佳匹配。
b. 类型转换匹配:如果有多个函数模板能够通过一系列的类型转换(如隐式类型转换)匹配实际参数的类型,那么转换次数最少的模板将被选择为最佳匹配。
c. 模板特化匹配:如果存在与调用参数类型完全匹配的模板特化,那么它将被选择为最佳匹配。
d. 不匹配:如果没有找到合适的模板来匹配调用参数的类型,那么将导致编译错误。
-
函数模板的特例化规则:当函数模板的特化版本和常规模板同时存在时,编译器会优先选择特化版本。
下面的代码展示了函数模板匹配原则的应用:
template <typename T>
void print(T value) {std::cout << value << std::endl;
}template <>
void print(int value) {std::cout << "Specialized: " << value << std::endl;
}void print(double value) {std::cout << "Non-template: " << value << std::endl;
}int main() {print(5); // 调用特化版本的 print,输出 "Specialized: 5"print(3.14); // 调用非模板函数 print,输出 "Non-template: 3.14"print("Hello"); // 调用普通模板函数 print,输出 "Hello"return 0;
}
在上述示例中,当调用 print
函数时,根据参数类型的不同,编译器将根据匹配原则选择最佳匹配的函数版本。如果有特化版本,将优先选择特化版本。如果没有特化版本,会选择模板函数中最适合的版本来实例化。
三、类模板
1. 类模板的定义格式
定义多个类型
template<class T1, class T2, ..., class Tn>
class 类模板名
{// 类内成员定义
};
定义单个类型
template <typename T>
class ClassName {// 类模板的成员和方法声明及定义
};
在上述格式中,template <typename T>
表示定义了一个类模板,T
是一个类型参数,它可以在类的成员和方法中使用。你可以根据需要使用其他的类型参数名称。
下面的代码展示了一个简单的类模板的定义:
🚨注意:Pair 不是具体的类,是编译器根据被实例化的类型生成具体类的模具template <typename T>
class Pair {
private:T first;T second;public:Pair(T f, T s) : first(f), second(s) {}T getFirst() const {return first;}T getSecond() const {return second;}void setFirst(T f) {first = f;}void setSecond(T s) {second = s;}
};
Pair
是一个类模板,拥有两个泛型成员变量 first
和 second
,以及一些泛型成员函数。通过类模板,我们可以定义一个通用的配对(Pair)类,用于存储任意类型的一对值。
2. 类模板的实例化
⭕类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。
例如:
int main() {Pair<int> p(3, 4); // 实例化一个 Pair 类型对象,其中 T 被替换为 intPair<double> q(1.5, 2.7); // 实例化一个 Pair 类型对象,其中 T 被替换为 doubleint first = p.getFirst(); // 获取 p 对象中的第一个值 3double second = q.getSecond(); // 获取 q 对象中的第二个值 2.7p.setSecond(7); // 设置 p 对象的第二个值为 7return 0;
}
在上面的代码中,我们使用不同的具体类型参数实例化了 Pair
类模板,并使用相应的对象进行操作。编译器会根据实际传递的类型参数替换类模板中的类型参数 T
,生成对应的类定义和对象实例化。
四、非类型模板参数
1. 概念
非类型模板参数是指在C++中,模板参数可以不仅仅是类型,还可以是常量表达式。非类型模板参数允许在模板实例化时传递常量值作为参数,并在编译时对其进行计算和使用。
⭕通过使用非类型模板参数,可以实现在编译时生成特定类型或值的代码。
🚨非类型模板参数必须是以下几种类型之一:
- 整数类型,包括整数、字符和枚举类型。
- 指针类型。
- 引用类型。
⭕浮点数、类对象和字符串是不允许作为非类型模板参数的。
因为非类型模板参数在编译时需要被计算和处理,而浮点数、类对象和字符串类型的计算和处理是在运行时进行的,无法在编译时确定。
2. 定义
在定义模板时,可以使用非类型模板参数来指定一个或多个参数。例如:
template <typename T, int SIZE>
class Array {T data[SIZE];// ...
};
在这个例子中,模板参数SIZE
是一个非类型的整数参数,用于指定数组的大小。在实例化Array
模板时,需要指定一个整数常量作为SIZE
的值。
五、模板的特化
1. 概念
通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结果,需要特殊处理。比如:实现了一个专门用来进行小于比较的函数模板。
// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{return left < right;
}int main()
{cout << Less(1, 2) << endl; // 可以比较,结果正确Date d1(2022, 7, 7);Date d2(2022, 7, 8);cout << Less(d1, d2) << endl; // 可以比较,结果正确Date* p1 = &d1;Date* p2 = &d2;cout << Less(p1, p2) << endl; // 可以比较,结果错误return 0;
}
可以看到,Less
绝对多数情况下都可以正常比较,但是在特殊场景下就得到错误的结果。上述示例中,p1
指向的d1
显然小于p2
指向的d2
对象,但是Less
内部并没有比较p1
和p2
指向的对象内容,而比较的是p1
和p2
指针的地址,这就无法达到预期而错误。此时,就需要对模板进行特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化。
2. 函数模版特化
函数模板的特化步骤:
- 必须要先有一个基础的函数模板
- 关键字template后面接一对空的尖括号<>
- 函数名后跟一对尖括号,尖括号中指定需要特化的类型
- 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
// 函数模板 -- 参数匹配
template<class T>
bool Less(T left, T right)
{return left < right;
}// 对Less函数模板进行特化
template<>
bool Less<Date*>(Date* left, Date* right)
{return *left < *right;
}
int main()
{cout << Less(1, 2) << endl;Date d1(2022, 7, 7);Date d2(2022, 7, 8);cout << Less(d1, d2) << endl;Date* p1 = &d1;Date* p2 = &d2;cout << Less(p1, p2) << endl; // 调用特化之后的版本,而不走模板生成了
return 0;
}
3. 类模版特化
⭕全特化
完全特化是指对于特定的类型或参数,提供了一个完全定制的模板实现。在完全特化中,给定的特化版本针对特定类型或参数提供了特定的实现,这个特化版本将完全替代泛化模板。可以通过显式声明来完成完全特化,使用 template<>
来指示特化版本。
下面是一个完全特化的示例:
template <typename T>
struct MyClass {void doSomething() {// 泛化版本的实现}
};template <>
struct MyClass<int> {void doSomething() {// 针对 int 类型的完全特化实现}
};
在上面的示例中,我们定义了一个模板类 MyClass
,并对其进行了完全特化。在 MyClass<int>
的特化版本中,我们针对 int
类型提供了一个特定的成员函数实现。
⭕偏特化
偏特化是指对部分类型或参数进行特化,针对特定的形式或范围进行自定义处理。偏特化可以有多个参数,并对其中一个或多个参数进行特化。相对于完全特化,偏特化可以提供更灵活的定制需求。
下面是一个偏特化的示例:
template <typename T, typename U>
struct MyClass {void doSomething() {// 泛化版本的实现}
};template <typename T>
struct MyClass<T, int> {void doSomething() {// 对于第二个参数为 int 的偏特化实现}
};
在上面的示例中,我们定义了一个模板类 MyClass
,并对其进行了偏特化。在 MyClass<T, int>
的特化版本中,我们针对第二个参数为 int
的情况提供了一个特定的成员函数实现。
🚨注意,特化版本的成员函数可以是不同的,甚至可以有不同的成员变量和特定的行为。
4. 模版特化应用示例
下面是一个使用函数模板特化的示例,展示了如何实现针对特定类型的特定行为:
#include <iostream>// 泛化版本的模板函数
template <typename T>
void showType(T value) {std::cout << "Value: " << value << " is of unknown type\n";
}// 特化版本的模板函数,针对字符串类型
template <>
void showType<std::string>(std::string value) {std::cout << "Value: " << value << " is a string\n";
}// 特化版本的模板函数,针对整型类型
template <>
void showType<int>(int value) {std::cout << "Value: " << value << " is an integer\n";
}int main() {showType("Hello"); // 使用特化版本的模板函数,输出 "Value: Hello is a string"showType(123); // 使用特化版本的模板函数,输出 "Value: 123 is an integer"showType(3.14); // 使用泛化版本的模板函数,输出 "Value: 3.14 is of unknown type"return 0;
}
在上述示例中,我们定义了一个模板函数 showType
,用于根据传入的参数类型显示该值的类型信息。
通过模板特化,我们为特定类型(std::string
和int
)提供了特定的实现方式。在主函数中,我们分别调用了 showType
函数并传入不同的参数类型,从而分别调用了泛化版本和特化版本的模板函数。
这样我们可以根据不同的类型提供特定的处理方式,以满足特定需求。运行结果为:
Value: Hello is a string
Value: 123 is an integer
Value: 3.14 is of unknown type
六、模板分离编译
1. 什么是分离编译
一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。
2. 模版的分离编译
模板的分离编译可以将模板的声明和实现分离到不同的文件,这样每个源文件只需要编译一次模板的实现,减少了代码冗余和编译时间。
🚨🚨注意:每次进行编译都要进行模版实例化,如果不实例化或者参数与实例化的不匹配,编译器在进行链接时会报错,所以不建议进行分离编译
下面是一个使用模板的分离编译的示例:
- 头文件
mytemplate.h
包含了模板的声明:
#ifndef MYTEMPLATE_H
#define MYTEMPLATE_Htemplate <typename T>
class MyClass {
public:void print(T value);
};#endif
- 源文件
mytemplate.cpp
包含了模板的实现:
#include <iostream>
#include "mytemplate.h"template <typename T>
void MyClass<T>::print(T value) {std::cout << "Value: " << value << std::endl;
}// 显式实例化模板,以确保编译器生成该类型的代码
template class MyClass<int>;
- 主函数所在的源文件
main.cpp
使用了模板,但没有包含实现:
#include "mytemplate.h"int main() {MyClass<int> obj;obj.print(42);return 0;
}
在上述示例中,mytemplate.h
包含了模板的声明,mytemplate.cpp
包含了模板的实现,并且通过使用 template class MyClass<int>
显式实例化了模板的 int
特化版本(必须要实例化,否则就会报错)
七、模版的优缺点
【优点】
-
通用性:模板提供了一种通用的编程方式,可以在不同的类型上进行操作和实例化,增强了代码的复用性和可扩展性。
-
静态类型检查:模板在编译时进行类型检查,可以捕获一些类型错误和逻辑错误,提前发现问题并减少运行时错误。
-
高性能:模板生成的代码在编译时会生成特定类型的实现,避免了运行时的类型转换和动态分派,提供了更高的执行效率。
-
泛化算法:模板可以用于实现各种泛化算法,无需为不同的数据类型编写不同的代码,减少了重复劳动和代码维护成本。
【缺点】
-
长编译时间:模板通常在编译时进行实例化和展开,对于复杂的模板和大规模的代码库,编译时间可能会显著增加。
-
可读性差:模板的代码通常比非模板代码更复杂,对于初学者或不熟悉模板编程的人来说,理解和维护模板代码可能更加困难。
-
编译错误信息难以理解:当模板出现编译错误时,编译器生成的错误消息可能很难理解和定位,给调试带来一定的困难。
-
扩展性受限:对于已实例化的模板,无法在运行时动态地添加新的类型支持,如果需要支持新的类型或功能,需要重新编译模板。
温馨提示
感谢您对博主文章的关注与支持!另外,我计划在未来的更新中持续探讨与本文相关的内容,会为您带来更多关于C++以及编程技术问题的深入解析、应用案例和趣味玩法等。请继续关注博主的更新,不要错过任何精彩内容!
再次感谢您的支持和关注。期待与您建立更紧密的互动,共同探索C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!
相关文章:
【C++入门到精通】C++入门 —— 模版(template)
阅读导航 前言一、模版的概念二、函数模版1. 函数模板概念2. 函数模板定义格式3. 函数模板的原理4. 函数模版的实例化🚩隐式实例化🚩显式实例化 5. 函数模板的匹配原则 三、类模板1. 类模板的定义格式2. 类模板的实例化 四、非类型模板参数1. 概念2. 定义…...
ARM汇编【3】:LOAD/STORE MULTIPLE PUSH AND POP
LOAD/STORE MULTIPLE 有时一次加载(或存储)多个值更有效。为此,我们使用LDM(加载多个)和STM(存储多个)。这些指令有一些变化,基本上只在访问初始地址的方式上有所不同。这是…...
Python之Qt输出UI
安装PySide2 输入pip install PySide2安装Qt for Python,如果安装过慢需要翻墙,则可以使用国内清华镜像下载,输入命令pip install --user -i https://pypi.tuna.tsinghua.edu.cn/simple PySide2,如下图, 示例Demo i…...
【1day】复现泛微OA某版本SQL注入漏洞
目录 一、漏洞描述 二、影响版本 三、资产测绘 四、漏洞复现 一、漏洞描述 泛微e-cology是一款由泛微网络科技开发的协同管理平台,支持人力资源、财务、行政等多功能管理和移动办公。泛微OA存在SQL注入漏洞,攻击者利用Web应用程序对用户输入验证上的疏忽,在输入的数据…...
安卓系列机型-禁止卸载某个APP 防止误卸载软件 无需root权限
安卓系列机型-禁止安装某软件 防止“沉迷游戏的小孩”操作解析_安卓机器的博客-CSDN博客 上一期讲了如何禁止安装某个app。今天讲下如何禁止卸载某app。正好相反的操作。任何操作有利有弊。主要看使用者如何对待使用。 💔💔💔以腾讯的一款游…...
【算法系列篇】二分查找——这还是你所知道的二分查找算法吗?
文章目录 前言什么是二分查找算法1.二分查找1.1 题目要求1.2 做题思路1.3 Java代码实现 2.在排序数组中查找元素的第一个和最后一个位置2.1 题目要求2.2 做题思路2.3 Java代码实现 3.搜索插入位置3.1 题目要求3.2 做题思路3.3 Java代码实现 4.x的平方根4.1 题目要求4.2 做题思路…...
【前端从0开始】JavaSript——分支流程控制
流程控制 在任何一门程序设计语言中,都需要支持满足程序结构 化所需要的三种流程控制: ●顺序控制 ●分支控制(条件控制) ●循环控制 顺序控制:在程序流程控制中,最基本的就是顺序控制。程序会按照自上而下的顺序执行…...
Linux权限
Linux中一切皆文件,那么文件就应该有相对于的类型,而在Linux当中,类型不是直接看后缀来决定的。 -普通文件、文本、可执行、归档文件等d目录b块设备、block、磁盘c字符设备、键盘、显示器p管道文件s网络socket文件l链接文件 link 然后后面的九…...
PMP如何备考?学习方式这里有
预习阶段:强烈建议跟着习课视频学习(自己看书真的很难看懂),初步了解PMBOK,有个大致印象; 精讲阶段:这个时候就需要静下心来深入了解各个知识模块,不仅是看PMBOK,还要尽…...
【Java转Go】快速上手学习笔记(四)之基础篇三
目录 泛型内置泛型的使用切片泛型和泛型函数map泛型泛型约束泛型完整代码 接口反射协程特点WaitGroupgoroutine的调度模型:MPG模型 channel介绍语法:举例:channel遍历基本使用和协程一起使用案例一案例二 select...casemain.go 完整代码 文件…...
vue中form和table标签过长
form标签过长 效果: 代码: <el-form-item v-for"(item,index) in ticketEditTable1" :label"item.fieldNameCn" :propitem.fieldName :key"item.fieldNameCn" overflow"":rules"form[item.fieldName…...
java基础复习(第七日)
java基础复习(七) 1.MQ如何避免消息重复投递或重复消费? 在消息生产时,MQ 内部针对每条生产者发送到消息生成一个 inner-msg-id,作为去重的依据(消息投递失败并重传),避免重复的消息进入队列;…...
day24 | 理论基础、77. 组合
目录: 解题及思路学习 理论基础 回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案,如果想让回溯法高效一些,可以加一些剪枝的操作,但也改不了回溯法就是穷举的本质。 回溯法,一般可以…...
数据结构(1)
数据结构其实就是将数据按照一定的关系组织起来的集合,用于组织和存储数据。 1.数据结构分类 1.逻辑结构 逻辑结构是从具体问题中抽象出来的模型,是抽象意义的结构,按照对象中数据的相互关系进行分类。 1>集合结构:集合结构中…...
10个非常有用的Python库,你知道几个?
整理|TesterHome 这里给大家介绍10个不是最流行但非常有用的Python库,希望可以提供参考帮助。 PyO3 PyO3是一个Rust库,可以让你在Rust中编写Python模块。它可以利用 Rust 的速度和安全性编写高性能的 Python 模块。 https://github.com/PyO3…...
linux安装 MySQL8 并配置开机自启动
目录 1.下载 mysql 安装包 2.上传并解压 mysql 3.修改 mysql 文件夹名 4.创建mysql 用户和用户组 5.数据目录 (1)创建目录 (2)赋予权限 6.初始化mysql (1)配置参数 (2)配置环…...
MySQL视图
一、视图-介绍及基本语法 视图(View)是一种虚拟存在的表。视图中的数据并不在数据库中实际存在,行和列数据来自定义视图的查询中使用的表,并且是在使用视图时动态生成的。 通俗的讲,视图只保存了查询的SQL逻辑…...
Pytorch-day05-可视化-checkpoint
PyTorch 可视化 1、模型结构可视化2、训练过程可视化3、模型评估可视化 #导入常用包 import os import numpy as np import torch from torch import nn from torch.utils.data import Dataset, DataLoader from torchvision.transforms import transforms import torchvis…...
实训笔记8.23
8.23笔记 8.23笔记一、Hive中函数1.1 Hive中内置函数1.1.1 数学函数1.1.2 字符串函数1.1.3 日期函数1.1.4 条件函数1.1.5 特殊函数 1.2 Hive的自定义函数1.2.1 自定义UDF1.2.2 自定义UDTF 二、Hive的压缩机制三、数据同步工具Sqoop的安装和使用3.1 sqoop的概念3.2 sqoop的核心功…...
2023年菏泽市中职学校技能大赛“网络安全”赛项规程
2023年菏泽市中职学校技能大赛 “网络安全”赛项规程 一、赛项名称 赛项名称:网络安全 赛项所属专业大类:信息技术类 二、竞赛目的 通过竞赛,检验参赛选手对网络、服务器系统等网络空间中各个信息系统的安全防护能力,以及分析…...
Android 13 - Media框架(6)- NuPlayer
上一节我们通过 NuPlayerDriver 了解了 NuPlayer 的使用方式,这一节我们一起来学习 NuPlayer 的部分实现细节。 ps:之前用 NuPlayer 播放本地视频很多都无法播放,所以觉得它不太行,这两天重新阅读发现它的功能其实很全面ÿ…...
机器学习|DBSCAN 算法的数学原理及代码解析
机器学习|DBSCAN 算法的数学原理及代码解析 引言 聚类是机器学习领域中一项重要的任务,它可以将数据集中相似的样本归为一类。DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一种是一种经典的密度聚类…...
用NUXT.JS,轻松搞定SEO!
nuxt.js 是什么? 如果你正在准备开发一个SEO友好的新项目,而且准备用 vue 开发,那么恭喜你,用 nuxt 是一个成本和效率都比较优秀的方案。 官方文档 知识中心案例 简单介绍下背景,这是一个专门为氚云低代码平台引流…...
什么是电商RPA?电商RPA能解决什么问题?电商RPA实施难点在哪里?
RPA机器人可以应用于各个行业和领域,例如金融、保险、制造、物流、电商等。它可以减少人工错误和重复工作,提高效率和生产力。RPA还可以在处理大量数据时加快处理速度,提供更准确和可靠的结果。此外,RPA还可以为员工提供更有价值的…...
【BUG】Docker启动MySQL报错
个人主页:金鳞踏雨 个人简介:大家好,我是金鳞,一个初出茅庐的Java小白 目前状况:22届普通本科毕业生,几经波折了,现在任职于一家国内大型知名日化公司,从事Java开发工作 我的博客&am…...
Spring Boot通过企业邮箱发件被Gmail退回的解决方法
这两天给我们开发的Chrome插件:Youtube中文配音 增加了账户注册和登录功能,其中有一步是邮箱验证,所以这边会在Spring Boot后台给用户的邮箱发个验证信息。如何发邮件在之前的文章教程里就有,这里就不说了,着重说说这两…...
Windows使用MobaXterm远程访问ubuntu20.04桌面
参考ubuntu 2020.4 安装vnc 一、脚本文件 remote_setup.sh脚本文件内容: #! /bin/bash #参考链接:https://blog.csdn.net/hailangdeyingzi/article/details/124507304 sudo apt update sudo apt install x11vnc -y sudo x11vnc -storepasswd telpo.12…...
C++注释风格
1. 文件头注释 每个文件都应该开始于一个注释块,描述文件的目的、作者、创建日期和版权信息。 /** FileName: MyClass.cpp* Purpose: Provides functionality for XYZ operations.* Author: [Your Name]* Creation Date: YYYY-MM-DD* Last Updated: YYYY-MM-DD* C…...
Linux 编译内核模块出现--Unknown symbol mcount
文章目录 Linux suse: # cat /etc/os-release NAME"SLES" VERSION"12-SP2" VERSION_ID"12.2" PRETTY_NAME"SUSE Linux Enterprise Server 12 SP2" ID"sles" ANSI_COLOR"0;32" CPE_NAME"cpe:/o:s…...
Pywin32 Cookbook by Eric
Writing Prompt 现在你是一名专业的Python工程师,请你根据"Pywin32_Funtion"函数的功能,为其编写一个清晰的文档说明Functions win32gui.GetWindowDC(hwnd) 描述 win32gui.GetWindowDC()函数用于获取指定窗口的设备上下文(Devi…...
做网站 域名是怎么回事/app开发成本预算表
http://blog.chinaunix.net/uid-15007890-id-3064254.html uptime11:35:08 up 21:57, 6 users, load average: 4.66, 2.98, 1.79“load”用来表示运行队列,用top 命令我们可以看到CPU一分钟,5分钟和15分钟内的运行队列的大小。这个值越大表明系统负荷越…...
引航博景网站做的很好吗/java培训机构十强
main.sh 主控制脚本#!/bin/bash# 是否发送邮件的开关(维护模式下我们需要关闭此功能,监控还是继续,但不发任何邮件。)export send1# 过滤ip地址(一旦报警,需要需要知道是哪台机器的IP,没有服务端,全部都是独立运行的。…...
博乐建设工程信息网站/如何做线上推广
windows 提示缺少VCRUNTIME140.dll错误一 原因最新在系统安装一些软件发现提示这是因为缺少了一下windows运行需要的库二 解决方法一链接: https://pan.baidu.com/s/1zGeWckZLaSQOwoBlEBxh-Q 提取码: hfsh进去下载:微软常用运行库合集_2019.07.20_X64.exe这个东西&a…...
德阳哪里有做网站的/广州seo关键字推广
Easy-表格数据1CodeNamePrice001name12323002name24612003name34612通过数据请求创建表格看到URL:属性 在背后解析时就是一个AJAX $.GET(XXXX)定义表格,并且通过url访问json数据, fitColumns:true表示自动适应,singleSelect:true 表示选中单个…...
郑州网站制作天强科技/站长之家 seo查询
转自:MongoDB常用28条查询语句 1、查询所有记录 db.userInfo.find(); 相当于: select* from userInfo; 默认每页显示20条记录,当显示不下的情况下,可以用it迭代命令查询下一页数据。注意:键入it命令不能带“࿱…...
机关网站机制建设情况/seo优化包括
寄生组合式继承 定义:所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其背后的基本思路是:不必为了指定子类型的原型而调用超类型的构造函数,我们所需要的无非就是超类型原型的一…...