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

C++初阶之模板深化讲解

在这里插入图片描述

模板深化讲解

  • 非类型模板
  • 模板的特化
    • 1.函数模板特化
    • 2.类模板特化
  • 模板分离编译
    • 1.什么是分离编译
    • 2.模板的分离编译
  • 模板总结

非类型模板

在这里插入图片描述

非类型模板(Non-Type Template)是 C++ 中的一种模板形式,它允许你在模板中传递除了类型以外的其他值,比如整数、枚举、指针等。这些参数可以在编译时被解析,用于生成模板的实例化版本。

非类型模板参数(Non-Type Template Parameter)是在模板声明中,作为参数的一部分,而不是类型的一部分。它们可以是常量表达式,例如整数常量、枚举、指针、引用等。非类型模板参数的值必须在编译时是已知的,因为它们用于生成模板的特定实例。

模板参数分类类型形参与非类型形参。

类型形参:出现在模板参数列表中,跟在class或者typename之类的参数类型名称。
非类型形参:就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。
使用非类型模板参数的一个典型示例是实现一个固定大小的数组容器,其中数组的大小在编译时由模板参数指定。

例如,以下是一个使用非类型模板参数的示例:

template <typename T, int Size>
class FixedSizeArray {
private:T data[Size];
public:// ...
};// 实例化一个大小为 10 的 FixedSizeArray,存储 int 类型
FixedSizeArray<int, 10> intArray;

在这个示例中,int Size 就是一个非类型模板参数,它确定了 FixedSizeArray 的数组大小。在编译时,intArray 的大小将被确定为 10,因为在实例化时传递了 10Size 参数。

注意:

  1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的
  2. 非类型的模板参数必须在编译期就能确认结果。

模板的特化

模板的特化Template Specialization)是 C++ 中一种允许你为特定类型或特定条件提供定制化实现的机制。模板特化允许你在针对某些特定类型或条件的情况下,提供一个专门的模板实现,以覆盖通用模板的行为。

在一般的模板中,你可能会为不同类型的数据提供一个通用的模板实现。然而,在某些情况下,特定类型可能需要特殊处理或定制化的行为。这时,你可以通过模板特化来为这些特定类型提供特殊实现。

模板特化分为两种类型:

类模板特化(Class Template Specialization):针对特定类型的特殊实现。
函数模板特化(Function Template Specialization):针对特定类型的特殊实现。

在某些情况下,如果没有使用模板的特化,可能会导致编译器选择错误的实现,从而产生不明确的错误或意外的行为。一个常见的情况是函数模板的参数匹配不明确,编译器无法确定应该使用哪个模板实现。这种情况下,特化可以提供明确的信息,帮助编译器正确选择实现。

以下是一个示例,展示在没有模板特化的情况下可能会发生的问题:

#include <iostream>template <typename T>
void printType(T value) {std::cout << "Generic template" << std::endl;
}// 模板特化版本,针对 int 类型
template <>
void printType<int>(int value) {std::cout << "Specialized template for int" << std::endl;
}int main() {int num = 5;printType(num);  // 编译错误,不明确的调用return 0;
}

在上面的示例中,没有模板特化的情况下,编译器无法确定应该调用哪个模板实现,因为 printType 函数可以接受任何类型的参数。这会导致编译错误,因为编译器无法根据上下文来选择正确的实现。

通过为 printType<int> 提供特化版本,我们可以明确告诉编译器在处理 int 类型参数时应该使用哪个实现。这种情况下,特化可以解决不明确调用的问题,确保编译器选择正确的实现。

1.函数模板特化

函数模板的特化步骤:

  1. 必须要先有一个基础的函数模板
  2. 关键字template后面接一对空的尖括号<>
  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
  4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。

函数模板特化的示例:

#include <iostream>// 通用的函数模板
template <typename T>
T add(T a, T b) {return a + b;
}// 函数模板的特化版本,针对 int 类型
template <>
int add(int a, int b) {std::cout << "Specialized version for int" << std::endl;return a + b + 10;
}int main() {int result1 = add(5, 3);    // 调用通用版本std::cout << "Result 1: " << result1 << std::endl;  // 输出: 8int result2 = add<int>(5, 3);  // 调用特化版本std::cout << "Result 2: " << result2 << std::endl;  // 输出: Specialized version for int// Result 2: 18return 0;
}

在上面的示例中,add 是一个函数模板,用于计算两个数的和。通过为 add<int> 提供特化版本,我们覆盖了默认的行为,并在特定类型下进行了定制化的操作。在 main 函数中,我们展示了如何调用通用版本和特化版本,以及它们的输出结果。

注意一般情况下如果函数模板遇到不能处理或者处理有误的类型,为了实现简单通常都是将该函数直接给出。

2.类模板特化

在 C++ 中,类模板特化是指为特定类型或条件提供定制化的类模板实现。有两种类型的类模板特化:全特化(Full Specialization)和偏特化(Partial Specialization)。

全特化Full Specialization):全特化是针对特定类型的完整特化。当你为某个特定类型提供了一个完整的特化版本,它将会覆盖通用的类模板定义。全特化的语法是在模板名后面添加 <> 并指定特化的类型。

示例:

// 通用的类模板
template <typename T>
class MyTemplate {
public:void print() {std::cout << "Generic Template" << std::endl;}
};// 类模板的全特化版本,针对 int 类型
template <>
class MyTemplate<int> {
public:void print() {std::cout << "Specialized Template for int" << std::endl;}
};

偏特化Partial Specialization):偏特化是在某些条件下对模板参数进行特化,通常用于更细粒度的定制。偏特化允许你为某些特定情况提供特化版本,而不是为每个类型提供完整的特化版本。偏特化的语法是在模板名后面添加 <>,并在尖括号中指定要特化的参数。

示例:

// 通用的类模板
template <typename T, typename U>
class Pair {
public:Pair(T first, U second) : first_(first), second_(second) {}void print() {std::cout << "Generic Pair: " << first_ << ", " << second_ << std::endl;}
private:T first_;U second_;
};// 类模板的偏特化版本,针对两个相同类型的参数
template <typename T>
class Pair<T, T> {
public:Pair(T first, T second) : first_(first), second_(second) {}void print() {std::cout << "Specialized Pair for same type: " << first_ << ", " << second_ << std::endl;}
private:T first_;T second_;
};

在上述示例中,全特化针对整型类型提供了特化版本,而偏特化则针对两个相同类型的参数提供了特化版本。

总结:

全特化:为特定类型提供完整特化版本。
偏特化:为特定情况或条件提供特化版本。

通过使用模板特化,你可以在需要定制化行为的情况下,为类模板提供精确的实现,增强了模板的灵活性和适用性。

类模板允许我们为不同的数据类型提供相同的代码结构,以适应多种类型的需求。然而,在某些情况下,编译器可能无法推断出如何比较不同类型的对象。这就是为什么在下面的示例中,如果不使用类模板的特化,编译器可能无法正确排序日期类的原因。

在默认情况下,std::sort 函数使用元素的<运算符来比较元素的大小。对于基本类型(如整数)或支持 < 运算符的类型,这是没有问题的。但对于自定义类型(如日期类),编译器无法知道如何执行比较。

通过使用类模板的特化,我们为不同类型的日期类提供了明确的比较逻辑,即 operator< 运算符的重载。这告诉编译器如何在特定情况下比较日期对象,使得 std::sort 函数能够正确工作。

特化类模板允许我们在需要的地方为特定类型提供定制的实现,从而解决编译器无法推断的问题,确保程序能够正确运行。

下面是一个关于日期类的示例,使用类模板特化来比较大小,并通过 std::sort 函数对日期进行排序
在示例中,我们定义了一个 Date 类模板,然后为 Date 类提供了特化版本,用于实现日期的比较操作:

#include <iostream>
#include <vector>
#include <algorithm>// 通用的类模板
template <typename T>
class Date {
public:Date(T year, T month, T day) : year_(year), month_(month), day_(day) {}// 比较运算符bool operator<(const Date& other) const {if (year_ != other.year_) return year_ < other.year_;if (month_ != other.month_) return month_ < other.month_;return day_ < other.day_;}void print() {std::cout << year_ << "-" << month_ << "-" << day_ << std::endl;}private:T year_;T month_;T day_;
};// 类模板的特化版本,用于 int 类型
template <>
class Date<int> {
public:Date(int year, int month, int day) : year_(year), month_(month), day_(day) {}bool operator<(const Date<int>& other) const {if (year_ != other.year_) return year_ < other.year_;if (month_ != other.month_) return month_ < other.month_;return day_ < other.day_;}void print() {std::cout << year_ << "-" << month_ << "-" << day_ << std::endl;}private:int year_;int month_;int day_;
};int main() {std::vector<Date<int>> dates = {{2022, 8, 15},{2021, 12, 25},{2022, 1, 1},{2022, 3, 20}};std::cout << "Before sorting:" << std::endl;for (const auto& date : dates) {date.print();}std::sort(dates.begin(), dates.end());std::cout << "After sorting:" << std::endl;for (const auto& date : dates) {date.print();}return 0;
}

在上面的示例中,我们首先定义了一个通用的 Date 类模板,用于存储年、月、日信息。然后,我们为 Date<int> 提供了特化版本,用于实现基于 int 类型的日期比较操作。

main 函数中,我们创建了一个存储日期的 std::vector,然后使用 std::sort 函数对日期进行排序。由于我们为 Date<int> 提供了特化版本,它能够正确比较日期并进行排序。最终,我们分别输出排序前后的日期顺序。

模板分离编译

1.什么是分离编译

分离编译Separate Compilation)是一种软件开发技术,它将一个大型程序分割成多个小的源代码文件,每个文件包含一个或多个相关的函数、类或变量的定义和实现。这些源代码文件可以在不同的编译单元中进行编译,然后在链接阶段将它们组合成一个可执行的程序。

分离编译的主要目标是提高代码的可维护性、编译速度和资源利用率。以下是分离编译的一些优点:

模块化开发:将程序分割成多个模块,每个模块负责一个特定的功能。这样,不同的开发人员可以独立地处理不同的模块,从而提高开发效率。
代码复用:在不同的项目中,可以重新使用已经编写并通过测试的模块,从而减少开发时间和资源。
编译速度:只有修改的模块需要重新编译,其他未修改的模块可以保持不变。这可以显著加快编译时间。
资源利用率:只有需要的模块会被编译,减少了不必要的编译和内存占用。

分离编译的基本流程如下:

编写模块:将程序分割成多个模块,并编写每个模块的定义和实现。
编译模块:分别编译每个模块的源代码,生成目标文件(例如 .obj.o 文件)。
链接模块:将所有目标文件链接在一起,解决引用关系,生成最终的可执行文件。

在分离编译中,头文件(.h 文件)通常用于存放函数和类的声明,而源文件(.cpp 文件)包含函数和类的实现。这种划分可以帮助编译器了解每个模块的接口和实现,从而在不同模块之间建立正确的链接。

分离编译是现代软件开发的重要实践,它有助于组织复杂的项目、提高开发效率,并降低维护成本。

2.模板的分离编译

在C++中,模板的分离编译是指将模板的声明和实现分开放置在不同的文件中。模板的声明通常放在头文件(.h.hpp 文件),而模板的实现则放在源文件(.cpp 文件)中。

模板的分离编译是为了解决链接时的模板实例化问题。C++编译器需要在使用模板的地方对模板进行实例化,但编译器在编译一个源文件时只能看到当前源文件的内容,无法知道其他源文件中模板的实现细节。因此,如果模板的声明和实现都放在头文件中,并且被多个源文件引用,会导致模板被多次实例化,最终在链接阶段会出现多个相同的实例化,引发重定义错误。

模板的分离编译定义的一般做法是:

将模板的声明放在头文件中(例如 .h 文件)。
将模板的实现放在源文件中(例如 .cpp 文件),并在源文件末尾包含模板的实现。

这样做的好处是,每个源文件只会对模板进行一次实例化,避免了重定义问题。
然而,模板的分离定义也可能引发一些问题,例如:

编译错误难以定位:如果模板的实现出现错误,编译器可能无法在使用模板的地方给出详细的错误信息,导致调试困难。
代码维护困难:模板的实现分散在多个源文件中,可能导致代码维护变得更加复杂,需要确保每个源文件的模板实现保持一致。
可读性下降:模板的实现被分离到源文件中,可能会降低代码的可读性和可理解性。

为了避免模板分离定义带来的问题,一些编程实践推荐将模板的声明和实现都放在头文件中,以便在使用模板的地方能够看到完整的实现细节。如果模板的实现较为复杂,可以通过将模板特化的方式来解决分离定义的问题。

举一个C++中的模板的分离定义的例子

这个示例演示了如果模板的声明和实现被分离到不同的文件中,可能会导致重定义错误。

假设我们有以下两个文件:

Stack.h(头文件,包含模板的声明):

#ifndef STACK_H
#define STACK_Htemplate <typename T>
class Stack {
public:Stack();void push(const T& value);T pop();private:T elements[10];int top;
};#include "Stack.cpp"#endif

Stack.cpp(源文件,包含模板的实现):

#ifndef STACK_CPP
#define STACK_CPPtemplate <typename T>
Stack<T>::Stack() : top(-1) {}template <typename T>
void Stack<T>::push(const T& value) {elements[++top] = value;
}template <typename T>
T Stack<T>::pop() {return elements[top--];
}#endif

这个示例中,我们尝试在头文件中包含了源文件 Stack.cpp。这可能会导致以下问题:

重定义错误:当多个源文件包含同一个头文件时,每个源文件都会包含 Stack.cpp 中的模板实现,从而在链接时引发重定义错误。
解决方法是,将模板的声明和实现都放在头文件中,或者使用模板的显式实例化(explicit instantiation)来避免重定义错误
显式实例化是一种告诉编译器在特定类型上进行模板实例化的方式,可以在源文件中使用以下语法来避免问题:

template class Stack<int>;
template class Stack<double>;
// 等等

这样可以确保模板只会在特定类型上进行一次实例化,避免了重定义错误。

虽然显式实例化可以解决模板的分离定义问题,但它也有一些潜在的弊端:

  1. 维护困难:如果代码中使用了多种不同的类型进行实例化,就需要在源文件中为每种类型都显式实例化一次。这可能会导致代码冗余,增加维护的难度,尤其在模板被广泛使用的大型项目中。
  2. 可读性降低显式实例化的语法相对较为繁琐,可能会降低代码的可读性。程序员需要了解这种特殊的语法并在源文件中进行适当的显式实例化。
  3. 影响编译时间:显式实例化会导致编译器在编译时生成模板的具体实例化代码,从而增加了编译时间。特别是在模板被大量使用的情况下,编译时间可能会显著增加
  4. 局限性显式实例化只适用于那些已知要在特定类型上进行实例化的模板。对于一些可能会在不同类型上使用的通用模板,需要为每个可能的类型都显式实例化,这可能不太实际。

综上所述,虽然显式实例化是解决模板分离定义问题的一种方法,但它可能会引入一些不便之处和潜在的问题。因此,一些项目中更倾向于将模板的声明和实现都放在头文件中,以避免这些问题。

模板总结

优点:

  1. 通用性和重用性模板允许编写通用的代码,适用于多种数据类型和数据结构。这种通用性促进了代码的重用,减少了编写重复代码的需求。
  2. 类型安全: 模板可以在编译时进行类型检查,确保在模板实例化时使用正确的数据类型。这有助于避免运行时的类型错误
  3. 性能优势模板生成的代码是在编译时根据实际类型生成的,因此没有函数调用的开销,可以在一定程度上提高性能。
  4. 泛型算法: C++标准库中的算法和容器都使用模板,使得开发人员能够方便地使用通用的排序、查找、遍历等算法
  5. 抽象和封装模板可以实现抽象数据类型,将数据结构和操作封装在一起,提供更高层次的抽象。
  6. 编译时错误检查模板的错误通常在编译时被检测到,使得开发人员能够及早发现和修复问题。

缺点:

  1. 编译时错误信息难以理解: 模板错误的编译器错误信息可能非常复杂,对于初学者来说可能难以理解。这可能增加了调试的难度
  2. 编译时间增加模板的使用可能导致编译时间增加,特别是在大型项目中。模板的实例化会在编译时生成多个版本的代码,可能导致编译器花费更多时间。
  3. 代码膨胀模板的实例化会导致生成多份相似的代码,可能增加可执行文件的大小。
  4. 可读性下降一些复杂的模板代码可能难以阅读和理解,尤其是涉及元编程技巧的情况。
  5. 维护困难: 当模板的实现被分离到不同的文件中,维护可能会变得困难,特别是涉及到显式实例化等情况。

综合考虑,模板是一个强大的工具,可以在很多情况下提供巨大的优势。然而,在使用模板时,开发人员需要权衡其优点和缺点,并根据具体情况做出合适的选择。

相关文章:

C++初阶之模板深化讲解

模板深化讲解 非类型模板模板的特化1.函数模板特化2.类模板特化 模板分离编译1.什么是分离编译2.模板的分离编译 模板总结 非类型模板 非类型模板&#xff08;Non-Type Template&#xff09;是 C 中的一种模板形式&#xff0c;它允许你在模板中传递除了类型以外的其他值&#x…...

Redis数据结构——整数集合

定义 整数集合是集合的实现方式之一&#xff0c;当一个集合只包含整数值元素时&#xff0c;并且这个集合的元素数量不多时&#xff0c;Redis就会使用整数集合作为集合的底层实现。 整数集合就是存放整数的一个数组&#xff0c;整数集合的结构体定义&#xff1a; typeof struc…...

背上大书包准备面试之CSS篇

目录 H5 新特性 css3新特性&#xff1f; 为什么要初始化css样式&#xff1f; 浏览器兼容性问题&#xff1f; css sprites&#xff08;css精灵图&#xff09;&#xff1f; css盒模型是什么样的&#xff1f; 页面中一个块元素的宽度包含了盒模型中的哪些部分&#xff1f;…...

linux系列基本介绍

虽然我们常说Linux操作系统&#xff0c;这种叫法是不正确的&#xff0c;严格意义上讲&#xff0c;Linux并不是操作系统&#xff0c;而是属于操作系统的一个内核&#xff0c;inux内核提供了操作系统的核心功能&#xff0c;如进程管理、内存管理、文件系统等。 Linux有很多不同的…...

vue.draggable浅尝

介绍 Vue.Draggable是一款基于Sortable.js实现的vue拖拽插件。支持移动设备、拖拽和选择文本、智能滚动&#xff0c;可以在不同列表间拖拽、不依赖jQuery为基础、vue 2过渡动画兼容、支持撤销操作&#xff0c;总之是一款非常优秀的vue拖拽组件。本篇将介绍如何搭建环境及简单的…...

Tree相关

1.树相关题目 1.1 二叉树的中序遍历&#xff08;简单&#xff09;&#xff1a;递归 题目&#xff1a;使用中序遍历二叉树 思想&#xff1a;按照访问左子树——根节点——右子树的方式遍历这棵树&#xff0c;而在访问左子树或者右子树的时候我们按照同样的方式遍历&#xff0…...

git日常操作-案例

文章目录 查看远程有那些分支 查看远程有那些分支 git ls-remote --heads origin...

cmake链接.lib库

当使用CMake编写CMakeLists.txt文件来链接其他文件夹的.lib库文件时&#xff0c;你需要做以下几个步骤&#xff1a; 设置库文件路径&#xff1a; 使用 link_directories() 函数设置要搜索库文件的路径。例如&#xff1a; link_directories(${CMAKE_SOURCE_DIR}/path/to/libra…...

SkyWalking 部署(包含ES)

SkyWalking安装 结构 首先SkyWalking主要需要oapService、webApp、Elasticsearch&#xff08;可选存储&#xff09;三个&#xff0c;接下来讲一下这三个的安装步骤&#xff0c;安装过程中出现了一些细小的配置错误&#xff0c;导致用了快两天才弄好&#xff0c;麻木了&#x…...

Android学习之路(2) 设置视图

一、设置视图宽高 ​ 在Android开发中&#xff0c;可以使用LayoutParams类来设置视图&#xff08;View&#xff09;的宽度和高度。LayoutParams是一个用于布局的参数类&#xff0c;用于指定视图在父容器中的位置和大小。 ​ 下面是设置视图宽度和高度的示例代码&#xff1a; …...

SIP/VoIP之常见的视频问题

除了语音通话外&#xff0c;视频通话也是SIP协议通话中重要的功能&#xff0c;在实际应用中&#xff0c;经常会遇到一些视频问题&#xff0c;如下&#xff08;以h264为例&#xff09; 一、 己方未显示对方视频图像 排查方法&#xff1a; 查看网络抓包中有没有发给已方的视频…...

docker的服务/容器缺少vim问题

背景/问题&#xff1a; docker的服务/容器缺少vim问题 bash: vim: command not found 在docker的mysql服务中安装Vim 1、执行apt-get update root6d8d17e320a0:/# apt-get update问题:文件下载失败 Err:1 http://security.debian.org/debian-security buster/updates InRe…...

HCIP-OpenStack

1、OpenStack概述 OpenStack是一种云操作系统&#xff0c;OpenStack是虚拟机、裸金属和容器的云基础架构。可控制整个数据中心的大型计算、存储和网络资源池&#xff0c;所有资源都通过API或Web界面进行管理。 为什么称OpenStack是云操作系统&#xff1f; 云一般指云计算&…...

前端:Vue.js学习

前端:Vue.js学习 1. 第一个Vue程序2. Vue指令2.1 v-if、v-else-if、v-else2.2 v-for2.3 事件绑定 v-on:2.4 v-model 数据双向绑定2.5 v-bind 绑定属性 3. Vue组件4. Vue axios异步通信5. 计算属性6. 插槽 slots7. 自定义事件内容分发 1. 第一个Vue程序 首先把vue.js拷贝到本地…...

MySQL到Oracle快速上手

第一次做Oracle项目的时候对一些语法区别不太清楚&#xff0c;这里列出一些开发中发现的与MYSQL不同的点 一个用户相当于一个数据库 表空间 表空间是用于存储表、索引和其他数据库对象的逻辑存储结构。每个表空间由一个或多个数据文件组成&#xff0c;这些文件可以位于不同的物…...

机器人CPP编程基础-01第一个程序Hello World

很多课程先讲C/C或者一些其他编程课&#xff0c;称之为基础课程。然后到本科高年级进行机器人专业课学习&#xff0c;这样时间损失非常大&#xff0c;效率非常低。 C/单片机/嵌入式/ROS等这些编程基础可以合并到一门课中进行实现&#xff0c;这些素材已经迭代三轮以上&#xf…...

前后端分离------后端创建笔记(03)前后端对接(下)

本文章转载于【SpringBootVue】全网最简单但实用的前后端分离项目实战笔记 - 前端_大菜007的博客-CSDN博客 仅用于学习和讨论&#xff0c;如有侵权请联系 源码&#xff1a;https://gitee.com/green_vegetables/x-admin-project.git 素材&#xff1a;https://pan.baidu.com/s/…...

Leet code1049 最后一块石头的重量II

1049 最后一块石头的重量II 【问题描述】 有一堆石头&#xff0c;用整数数组 stones 表示。其中 stones[i] 表示第 i 块石头的重量。 每一回合&#xff0c;从中选出任意两块石头&#xff0c;然后将它们一起粉碎。假设石头的重量分别为 x 和 y&#xff0c;且 x < y。那么粉…...

Rust语法:变量,函数,控制流,struct

文章目录 变量可变与不可变变量变量与常量变量的Shadowing标量类型整数 复合类型 函数控制流if elseloop & whilefor in structstruct的定义Tuple Structstruct的方法与函数 变量 可变与不可变变量 Rust中使用let来声明变量&#xff0c;但是let声明的是不可变变量&#x…...

LVS简介及LVS-DR搭建

目录 一. LVS简介&#xff1a; 1.简介 2. LVS工作模式&#xff1a; 3. LVS调度算法&#xff1a; 4. LVS-DR集群介绍&#xff1a; 二.LVS-DR搭建 1.RS配置 1&#xff09;两台RS&#xff0c;需要下载好httpd软件并准备好配置文件 2&#xff09;添加虚拟IP&#xff08;vip&…...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

【Python】 -- 趣味代码 - 小恐龙游戏

文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽&#xff0c;大家好&#xff0c;我是左手python&#xff01; Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库&#xff0c;用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统

医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上&#xff0c;开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识&#xff0c;在 vs 2017 平台上&#xff0c;进行 ASP.NET 应用程序和简易网站的开发&#xff1b;初步熟悉开发一…...

MongoDB学习和应用(高效的非关系型数据库)

一丶 MongoDB简介 对于社交类软件的功能&#xff0c;我们需要对它的功能特点进行分析&#xff1a; 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具&#xff1a; mysql&#xff1a;关系型数据库&am…...

苍穹外卖--缓存菜品

1.问题说明 用户端小程序展示的菜品数据都是通过查询数据库获得&#xff0c;如果用户端访问量比较大&#xff0c;数据库访问压力随之增大 2.实现思路 通过Redis来缓存菜品数据&#xff0c;减少数据库查询操作。 缓存逻辑分析&#xff1a; ①每个分类下的菜品保持一份缓存数据…...

技术栈RabbitMq的介绍和使用

目录 1. 什么是消息队列&#xff1f;2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...

MySQL 8.0 事务全面讲解

以下是一个结合两次回答的 MySQL 8.0 事务全面讲解&#xff0c;涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容&#xff0c;并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念&#xff08;ACID&#xff09; 事务是…...

在 Spring Boot 中使用 JSP

jsp&#xff1f; 好多年没用了。重新整一下 还费了点时间&#xff0c;记录一下。 项目结构&#xff1a; pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...