【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. 函数模版的实例化

用不同类型的参数使用函数模板时,称为函数模板的实例化。函数模板的实例化过程可以分为两个步骤:模板参数推断和模板函数生成。

  1. 模板参数推断:编译器根据实际传入的参数类型推导出模板参数的具体类型。编译器会尝试根据传递的参数类型来匹配模板参数,并确定参数的具体类型。如果无法进行准确的匹配,则可能会产生模板参数推断失败的错误。

  2. 模板函数生成:根据推断出的模板参数类型,编译器生成特定类型的函数代码。编译器使用推断出的参数类型来替换函数模板中的模板参数,生成与传递的参数类型匹配的函数定义。

函数模板的实例化是在编译时完成的,它提前为不同的参数类型生成了不同的函数定义,以提高代码的重用性和执行效率。

⭕模板参数实例化分为:隐式实例化和显式实例化

🚩隐式实例化

下面是一个示例,展示函数模板隐式实例化的过程:

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. 函数模板的匹配原则

  1. 最佳匹配原则:编译器会尝试找到与调用参数最匹配的函数模板来实例化。在函数模板的候选函数中,编译器会根据实际参数类型进行以下规则的匹配:

    a. 完全匹配:如果有一个函数模板能够完全匹配实际参数的类型,那么它将被选择为最佳匹配。

    b. 类型转换匹配:如果有多个函数模板能够通过一系列的类型转换(如隐式类型转换)匹配实际参数的类型,那么转换次数最少的模板将被选择为最佳匹配。

    c. 模板特化匹配:如果存在与调用参数类型完全匹配的模板特化,那么它将被选择为最佳匹配。

    d. 不匹配:如果没有找到合适的模板来匹配调用参数的类型,那么将导致编译错误。

  2. 函数模板的特例化规则:当函数模板的特化版本和常规模板同时存在时,编译器会优先选择特化版本

下面的代码展示了函数模板匹配原则的应用:

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 是一个类模板,拥有两个泛型成员变量 firstsecond,以及一些泛型成员函数。通过类模板,我们可以定义一个通用的配对(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++中,模板参数可以不仅仅是类型,还可以是常量表达式。非类型模板参数允许在模板实例化时传递常量值作为参数,并在编译时对其进行计算和使用。

⭕通过使用非类型模板参数,可以实现在编译时生成特定类型或值的代码

🚨非类型模板参数必须是以下几种类型之一

  1. 整数类型,包括整数、字符和枚举类型。
  2. 指针类型。
  3. 引用类型。

浮点数、类对象和字符串是不允许作为非类型模板参数的
因为非类型模板参数在编译时需要被计算和处理,而浮点数、类对象和字符串类型的计算和处理是在运行时进行的,无法在编译时确定。

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内部并没有比较p1p2指向的对象内容,而比较的是p1p2指针的地址,这就无法达到预期而错误。此时,就需要对模板进行特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。模板特化中分为函数模板特化与类模板特化

2. 函数模版特化

函数模板的特化步骤:

  1. 必须要先有一个基础的函数模板
  2. 关键字template后面接一对空的尖括号<>
  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
  4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
// 函数模板 -- 参数匹配
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::stringint)提供了特定的实现方式。在主函数中,我们分别调用了 showType 函数并传入不同的参数类型,从而分别调用了泛化版本和特化版本的模板函数。

这样我们可以根据不同的类型提供特定的处理方式,以满足特定需求。运行结果为:

Value: Hello is a string
Value: 123 is an integer
Value: 3.14 is of unknown type

六、模板分离编译

1. 什么是分离编译

一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式。

2. 模版的分离编译

模板的分离编译可以将模板的声明和实现分离到不同的文件,这样每个源文件只需要编译一次模板的实现,减少了代码冗余和编译时间。

🚨🚨注意:每次进行编译都要进行模版实例化,如果不实例化或者参数与实例化的不匹配,编译器在进行链接时会报错,所以不建议进行分离编译

下面是一个使用模板的分离编译的示例:

  1. 头文件 mytemplate.h 包含了模板的声明:
#ifndef MYTEMPLATE_H
#define MYTEMPLATE_Htemplate <typename T>
class MyClass {
public:void print(T value);
};#endif
  1. 源文件 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>;
  1. 主函数所在的源文件 main.cpp 使用了模板,但没有包含实现:
#include "mytemplate.h"int main() {MyClass<int> obj;obj.print(42);return 0;
}

在上述示例中,mytemplate.h 包含了模板的声明,mytemplate.cpp 包含了模板的实现,并且通过使用 template class MyClass<int> 显式实例化了模板的 int 特化版本(必须要实例化,否则就会报错)

七、模版的优缺点

【优点】

  1. 通用性:模板提供了一种通用的编程方式,可以在不同的类型上进行操作和实例化,增强了代码的复用性和可扩展性。

  2. 静态类型检查:模板在编译时进行类型检查,可以捕获一些类型错误和逻辑错误,提前发现问题并减少运行时错误。

  3. 高性能:模板生成的代码在编译时会生成特定类型的实现,避免了运行时的类型转换和动态分派,提供了更高的执行效率。

  4. 泛化算法:模板可以用于实现各种泛化算法,无需为不同的数据类型编写不同的代码,减少了重复劳动和代码维护成本。

【缺点】

  1. 长编译时间:模板通常在编译时进行实例化和展开,对于复杂的模板和大规模的代码库,编译时间可能会显著增加。

  2. 可读性差:模板的代码通常比非模板代码更复杂,对于初学者或不熟悉模板编程的人来说,理解和维护模板代码可能更加困难。

  3. 编译错误信息难以理解:当模板出现编译错误时,编译器生成的错误消息可能很难理解和定位,给调试带来一定的困难。

  4. 扩展性受限:对于已实例化的模板,无法在运行时动态地添加新的类型支持,如果需要支持新的类型或功能,需要重新编译模板。

温馨提示

感谢您对博主文章的关注与支持!另外,我计划在未来的更新中持续探讨与本文相关的内容,会为您带来更多关于C++以及编程技术问题的深入解析、应用案例和趣味玩法等。请继续关注博主的更新,不要错过任何精彩内容!

再次感谢您的支持和关注。期待与您建立更紧密的互动,共同探索C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!

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

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

相关文章

动态规划之路径问题

路径问题 1. 不同路径&#xff08;medium&#xff09;2. 不同路径II&#xff08;medium&#xff09;3. 礼物最大值&#xff08;medium&#xff09;4. 下降路径最小和&#xff08;medium&#xff09;5. 最⼩路径和&#xff08;medium&#xff09;6. 地下城游戏&#xff08;hard&…

图床项目进度(一)——UI首页

1. 前言 前面我不是说了要做一个图床吗&#xff0c;现在在做ui。 我vue水平不够高&#xff0c;大部分参考b站项目照猫画虎。 vue实战后台 我使用ts&#xff0c;vite&#xff0c;vue3进行了重构。 当然&#xff0c;我对这些理解并不深刻&#xff0c;许多代码都是游离于表面&am…

基于决策树(Decision Tree)的乳腺癌诊断

决策树(DecisionTree)学习是以实例为基础的归纳学习算法。算法从--组无序、无规则的事例中推理出决策树表示形式的分类规则,决策树也能表示为多个If-Then规则。一般在决策树中采用“自顶向下、分而治之”的递归方式,将搜索空间分为若千个互不相交的子集,在决策树的内部节点(非叶…

Spring事务和事务传播机制

目录 一. Spring 中事务的实现 1. 编程式事务 2. 声明式事务 3. Transaction 参数的设置 4. Transaction 的隔离级别 5. Transaction 的工作原理 二. Spring 事务传播机制 七种事务传播机制 支持当前事务 不支持当前事务 嵌套事务 一. Spring 中事务的实现 1. 编程式事…

mmdetection基于 PyTorch 的目标检测开源工具箱 入门教程

安装环境 MMDetection 支持在 Linux&#xff0c;Windows 和 macOS 上运行。它需要 Python 3.7 以上&#xff0c;CUDA 9.2 以上和 PyTorch 1.8 及其以上。 1、安装依赖 步骤 0. 从官方网站下载并安装 Miniconda。 步骤 1. 创建并激活一个 conda 环境。 conda create --name…

Docker是什么?详谈它的框架、使用场景、优势

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 作者会持续更新网络知识和python基础知识&#xff0c;期待你的关注 目录 一、什么是 Docker&#xff1f; 二、Docker 的架构 1、Docker客户端 2、Docker守护进程 3、Docker镜像 4、Docker容器 5、Docker…

【业务功能篇76】微服务网关路由predicates断言条件-filters路由转换地址-跨域问题-多级目录树化层级设计-mybatisPlus逻辑删除

业务开发-基础业务-分类管理 启动renren-fast如果出现如下错误 -Djps.track.ap.dependenciesfalse 添加相关配置即可 分类管理 1.后端分类接口 JDK8特性&#xff1a;https://blog.csdn.net/qq_38526573/category_11113126.html 在后端服务中我们需要查询出所有的三级分类信…

汽车摩托车零部件出口管理ERP解决方案

近年来&#xff0c;随着全球经济的发展&#xff0c;人们对交通工具的需求增加&#xff0c;国内汽车、摩托车市场的不断扩大&#xff0c;以及国内制造技术的不断提高&#xff0c;中国汽车、摩托车零部件出口业务迎来了广阔的发展前景&#xff0c;带动了汽车配件和摩托车配件市场…

分布式下的session共享问题

首页我们确定在分布式的情况下session是不能共享的。 1.不同的服务&#xff0c;session不能共享&#xff0c;也就是微服务的情况下 2.同一服务在分布式情况&#xff0c;session同样不能共享&#xff0c;也会是分布式情况 分布式下session共享问题解决方案(域名相同) 1.session复…

IDEA常用插件之类Jar包搜索Maven Search

文章目录 IDEA常用插件之类Jar包搜索Maven Search说明安装插件使用方法1.搜索自己要搜的jar包2.根据类名搜索 IDEA常用插件之类Jar包搜索Maven Search 说明 它可以帮助用户快速查找和浏览Maven中央存储库中可用的依赖项和插件。它可以帮助用户更方便地管理项目依赖项。 安装…

idea 对JavaScript进行debug调试

文章目录 1.新增 JavaScript Debug 配置2.配置访问地址3.访问url. 打断点测试 前言 : 工作中接手别人的前端代码没有注释&#xff0c;看浏览器的network或者console切来切去&#xff0c;很麻烦&#xff0c;可以试试idea自带的javscript debug功能。 1.新增 JavaScript Debug 配…

『SEQ日志』在 .NET中快速集成轻量级的分布式日志平台

&#x1f4e3;读完这篇文章里你能收获到 如何在Docker中部署 SEQ&#xff1a;介绍了如何创建和运行 SEQ 容器&#xff0c;给出了详细的执行操作如何使用 NLog 接入 .NET Core 应用程序的日志&#xff1a;详细介绍了 NLog 和 NLog.Seq 来配置和记录日志的步骤日志记录示例&…

UE4/5Niagara粒子特效之Niagara_Particles官方案例:3.3->4.3

目录 3.3 Visibility Tag 左边的发射器&#xff1a; 发射器更新 粒子生成 粒子更新 右边的发射器 和左边发射器不同的地方 3.4 Texture Sampling 发射器更新 粒子生成 粒子更新 4.1Play Audio Per Particle 系统 第三个发射器 发射器更新 粒子生成 粒子更新 第二个…

1268. 搜索推荐系统

链接&#xff1a; 1268. 搜索推荐系统 题解&#xff1a; class Solution { public: struct Trie {Trie() {end false;next.resize(26, nullptr);}bool end;std::set<std::string> words;std::vector<Trie*> next; };void insert_trie(const std::string& w…

Android相机-HAL子系统

引言 应用框架要通过拍照预览摄像获得照片或者视频,就需要向相机子系统发出请求, 一个请求对应一组结果 一次可发起多个请求&#xff0c;并且提交请求是非阻塞的&#xff0c;始终按照接收的顺序以队列的形式先进先出地进行顺序处理 一个请求包含了拍摄和拍照配置的所有信息&…

【音视频处理】转编码H264 to H265,FFmpeg,代码分享讲解

大家好&#xff0c;欢迎来到停止重构的频道。 本期我们讨论音视频文件转编码&#xff0c;如将视频H264转H265等。 内容中所提及的代码都会放在GitHub&#xff0c;感兴趣的小伙伴可以到GitHub下载。 我们按这样的顺序展开讨论&#xff1a;​ 1、 编码的作用 2、 转编码的…

avue-ueditor中隐藏部分工具栏

项目中不需要那么多工具栏,只需要展示部分工具栏 <avue-ueditor v-model"content" v-bind"options" :customConfig"customConfig" :placeholder"placeholder"></avue-ueditor>//按需隐藏或者显示工具栏即可 props: {custo…

C语言基础之——操作符(上)

本篇文章&#xff0c;我们将展开讲解C语言中的各种常用操作符&#xff0c;帮助大家更容易的解决一些运算类问题。 这里提醒一下小伙伴们&#xff0c;本章知识会大量涉及到二进制序列&#xff0c;不清楚二进制序列的小伙伴&#xff0c;可以去阅读我的另一篇文章《数据在内存中的…

6、Spring_Junit与JdbcTemplate整合

Spring 整合 1.Spring 整合 Junit 1.1新建项目结构 1.2导入依赖 导入 junit 与 Spring 依赖 <!-- 添加 spring 依赖--> <dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version…

MySQL 8.0 多实例的配置应用

文章目录 同版本多实例配置部署、启动、连接 不同版本多实例配置初始化initialize-insecure 含义 启动 同版本多实例 配置 mkdir -p /data/330{7..9}/data chown -R mysql.mysql /data/* cat > /data/3307/my.cnf <<EOF [mysqld] usermysql basedir/usr/local/mysql …