【C++】—— 模板介绍

news2024/11/6 13:44:35

前言:

在之前的学习中,我们已经对几个常见的STL库容器进行了详细的讲解,并且进行了模拟实现帮助大家立即。接下来,我们要介绍的就是关于 “模板” 的基本知识。


目录

前言

(一)非类型模板参数

1、基本介绍

(二)类模板的特化

1、定义

2、使用场景

 3、 函数模板特化

4、类模板特化

1️⃣ 全特化

2️⃣ 偏特化

5、类模板特化应用示例

(三)模板的分离编译

1、什么是模板分离编译?

2、 模板的分离编译

3、 解决方法 

总结


前言

首先我们要了解什么是模板。简单来说,模板提供了一种通用的编程方式,可以用于实现泛型编程,从而使得程序更加灵活和可复用。它可以让我们编写可以适用于多种数据类型的函数和类。使用模板可以避免重复编写相似的代码,并且能够提高代码的复用性、可读性和可维护性。

在C++模板中,通常使用类模板和函数模板两种方式来实现泛型编程。在之前的文章中,我已经对泛型编程 ,函数模板 以及类模板 进行了相应的介绍,今天我们将学习的是 1. 非类型模板参数 2. 类模板的特化 3. 模板的分离编译 这三个方面的知识。如果大家对于之前的还有不理解的,大家可以参考我这篇文章 :模板初阶介绍

C++中的模板是一种非常强大的编程工具,可以帮助开发者更高效地编写通用代码。下面就来详细介绍一下C++中的模板。


(一)非类型模板参数

首先模板参数分为类型形参非类型形参

  1. 类型形参:简单点来说定义的就是类型,出现在模板参数列表中,跟在class或者typename之类的参数类型名称。
  2. 非类型形参:与类型参数对应的就是关于非类型参数。就是用一个常量作为类(函数)模板的一个参数,在类(函数)模板中可将该参数当成常量来使用。

那么什么时候用这个非类型参数呢?接下来利用代码为大家进行介绍:

#define N 20

template<class T>
class Arry
{
public:

private:
	T _a[N];
};

int main()
{
	Arry<int>aa1;   
	Arry<double>aa2;


	return 0;
}

解释

  1. 对于上述代码我们简单的实现一个静态的数组,依照之前的思维,我们可能就会像上述这样,定眼一看觉得没什么大问题,当我们想要 【int】的数据时就给出相应的int,其他的同理;
  2. 但是呢此时就出现一个问题,上述我们定义的 aa1 想要 10个数据,二 aa2 想要100个数据时,上述这样的写法还能满足对应的需求吗?答案很明显,此时 define 这样的一个宏就不满足条件了;
  3. 不知道大家是否感到十分熟悉这一块,就跟我们之前的 【typedef】一样,关于【typedef】的问题我们解决不了,因此就引入了模板参数(例如一个存int,一个存double)。

💨  因此为了解决上述这样的问题,我们就引入了非类型模板参数来解决这个问题!!!

1、基本介绍

  1. C++中的非类型模板参数允许用户在编译时提供参数来实例化模板。
  2. 非类型模板参数是在编译时确定的,不像类型模板参数那样在运行时确定,因此非类型模板参数具有一些和普通函数参数不同的特性。
  3. 一般而言,非类型模板参数都被定义为常量表达式(const expression)。常量表达式是一种在编译时可以完全计算出的表达式。
  4. 常量表达式必须在编译期间得到确定的值,这就意味着在编译时就必须知道它的值。将非类型模板参数定义为常量表达式可以确保其在编译时已经确定并且不会改变。

因此,上述代码我们修改为以下这样即为一个非类型模板参数:

template<class T, size_t N>
class Arry
{
private:
	T _a[N];
};

int main()
{
	Arry<int, 10>aa1;    
	Arry<double, 20>aa2;


	return 0;
}
  • 我们通过调试即可观察到:

  •  跟类型模板参数类似,在这里我们还可以给缺省值,类型模板参数的缺省值是类型,这里非类型模板参数即是常量。
template<class T, size_t N = 20>
class Arry
{
private:
	T _a[N];
};

int main()
{
	Arry<int, 10>aa1;   
	Arry<double, 20>aa2;

	return 0;
}

  • 需要记住一点的是这里的非类型模板参数是一个整形常量,当我们想要去对其进行赋值时此时就会发生报错
template<class T, size_t N = 20>
void func(const T& aa)
{
	N = 10;
}

int main()
{

	func(1);

	return 0;
}

 

  •  double类型此时也会报错,记住一定是【整形常量

  •  除了上述示例中的类模板外,非类型模板参数还可以用于函数模板和变量模板。下面是一个使用非类型模板参数的函数模板示例:
template <int N>
void printNTimes(const string& str) {
    for (int i = 0; i < N; ++i) {
        cout << str << endl;;
    }
}

int main()
{
   
    printNTimes<3>("Hello world"); // 输出三次 "Hello world"
    return 0;
}

【解释】

  1. 上述代码定义了一个打印字符串多次的函数模板 【printNTimes】,其中的模板参数也是一个整数 N
  2. 在函数实现中,我们使用了一个循环来打印字符串 N 次。在使用 【printNTimes 】函数时,我们通过指定模板参数 N 来确定要打印字符串的次数;
  3. 由于 N 是一个非类型模板参数,因此在函数调用时必须指定 N 的常量值。

  • 运行结果如下:

【注意】

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

【综上所诉】

  • 总之,非类型模板参数是C++中另一种有用的模板技术,可以让我们编写更加通用、灵活和高效的代码;
  • 在使用非类型模板参数时,需要注意其类型和常量表达式的限制,并且需要在模板定义和使用时都遵守这些规则。

(二)类模板的特化

1、定义

  1. 在 C++ 中,模板是可以具备通用性的,即针对不同类型的参数,可以用同一份模板代码来生成不同的执行代码,这就是所谓的模板泛化(template generic);
  2. 然而,某些时候,我们需要针对某些特定的类型参数,使用不同的实现代码。这种情况下,我们需要使用模板特化(template specialization)

2、使用场景

通常情况下,使用模板可以实现一些与类型无关的代码,但对于一些特殊类型的可能会得到一些错误的结 果,需要特殊处理。

  • 比如:实现了一个专门用来进行小于比较的函数模板
// 函数模板 -- 参数匹配
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; // 可以比较,结果正确
    
     return 0;
}

【解释】

  1. 以上我们对一个日期进行相应的比较(前提大家需要准备一个日期类);
  2. 我们可以发现结果也是正确的

 

  • 但是遇到以下比较时,是否还可以进行正常的比较呢?
    Date* p1 = &d1;
	Date* p2 = &d2;
	cout << Less(p1, p2) << endl; // 可以比较,结果错误

【解释 】

  1. 可以看到,Less绝对多数情况下都可以正常比较,但是在特殊场景下就得到错误的结果;
  2. 上述示例中,p1指 向的d1显然小于p2指向的d2对象,但是Less内部并没有比较p1和p2指向的对象内容,而比较的是p1和p2指 针的地址,这就无法达到预期而错误。

💨 此时,就需要对模板进行特化。即:在原模板类的基础上,针对特殊类型所进行特殊化的实现方式。

 //全特化
template<>
bool Less<Date*>(Date* left, Date* right)
{
	return *left < *right;
}
  • 当然,我们也可以向下面这样写:
bool Less(Date* left, Date* right)
{
	return *left < *right;
}

 但是,大家不能觉得它没有用,在某些方面它的作用是不容忽视的!!


 模板特化是指针对特定类型参数的一种特殊形式,它可以提供类型特异性的实现。当我们需要提供对某些特殊类型的支持时,可以通过特化来实现。

 💨 特化分为两种:类模板部分特化和函数模板特化

 3、 函数模板特化

函数模板的特化步骤:

  • 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;
}

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

bool Less(Date* left, Date* right)
{
     return *left < *right;
}

该种实现简单明了,代码的可读性高,容易书写,因为对于一些参数类型复杂的函数模板,特化时特别给 出,因此函数模板不建议特化。


4、类模板特化

1️⃣ 全特化

类模板全特化(full template specialization)指的是对一个类模板进行完全特化,即给定一组类型参数,将该类模板中的所有类型参数都替换成这组给定的类型参数。

例如,对于如下的类模板:

template<class T1, class T2>
class Data
{
public:
     Data() {cout<<"Data<T1, T2>" <<endl;}
private:
     T1 _d1;
     T2 _d2;
};
  • 那么在使用时,就只能使用 Data<int, int> d3; 这个特定类型的类了。

输出我们可以发现:

对于如下的类模板:

template<>
class Data<int, char>
{
public:
	Data() { cout << "Data<int, char>" << endl; }
private:
	int _d1;
	char _d2;
};
  • 那么在使用时,就只能使用 Data<int, char> d4; 这个特定类型的类了。

输出我们可以发现:

需要注意的是:

  • 全特化是针对整个类模板的,而非某个成员函数或成员变量;
  • 同时,全特化并不会影响到其他未特化的类型参数组合,因此在使用时仍需指定正确的类型参数。

2️⃣ 偏特化

类模板偏特化(partial specialization)是指在一个泛型类模板中,对某些模板参数进行特定的限制或约束,以便更好地适应某些特定场景的需求。它可以让我们在针对某些特殊情况的实现中,对泛型算法进行定制化、优化或替换。

比如对于以下模板类:

template<class T1, class T2>
class Data
{
public:
 Data() {cout<<"Data<T1, T2>" <<endl;}
private:
 T1 _d1;
 T2 _d2;
};

类模板偏特化有以下两种表现方式:

  1. 针对部分模板参数的特化:这种方式下,模板类的某些模板参数被特化,而其他模板参数保持不变。例如:
// 将第二个参数特化为int
template <class T1>
class Data<T1, int>
{
public:
 Data() {cout<<"Data<T1, int>" <<endl;}
private:
 T1 _d1;
 int _d2;
}; 

输出我们可以发现:

  • 2. 参数更进一步的限制 :偏特化并不仅仅是指特化部分参数,而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。

对于如下的类模板:


//两个参数偏特化为指针类型
template <typename T1, typename T2>
class Data <T1*, T2*>
{
public:
	Data() { cout << "Data<T1*, T2*>" << endl; }

private:
	T1 _d1;
	T2 _d2;
};
  • 那么在使用时,就只能使用 Data<int*, int*> d7;  这个特定类型的类了。

输出我们可以发现: 

 

 对于如下的类模板:

//两个参数偏特化为引用类型
template <typename T1, typename T2>
class Data <T1&, T2&>
{
public:
	Data(const T1& d1, const T2& d2)
		: _d1(d1)
		, _d2(d2)
	{
		cout << "Data<T1&, T2&>" << endl;
	}

private:
	const T1& _d1;
	const T2& _d2;
};
  • 那么在使用时,就只能使用 Data<int&, int&> d8(1, 2); 这个特定类型的类了。

 输出我们可以发现: 

需要注意的是:

  • 类模板偏特化不能重载函数,也不能包含成员函数外的非类型成员,否则会导致编译错误。 

5、类模板特化应用示例

 有如下专门用来按照小于比较的类模板Less:

template<class T>
struct Less
{
 bool operator()(const T& x, const T& y) const
 {
 return x < y;
 }
};
int main()
{
     Date d1(2022, 7, 7);
     Date d2(2022, 7, 6);
     Date d3(2022, 7, 8);
     vector<Date> v1;
     v1.push_back(d1);
     v1.push_back(d2);
     v1.push_back(d3);
     // 可以直接排序,结果是日期升序
     sort(v1.begin(), v1.end(), Less<Date>());
     vector<Date*> v2;
     v2.push_back(&d1);
     v2.push_back(&d2);
     v2.push_back(&d3);
 
     // 可以直接排序,结果错误日期还不是升序,而v2中放的地址是升序
     // 此处需要在排序过程中,让sort比较v2中存放地址指向的日期对象
     // 但是走Less模板,sort在排序时实际比较的是v2中指针的地址,因此无法达到预期
     sort(v2.begin(), v2.end(), Less<Date*>());
     return 0;
}

解释:

  1. 通过观察上述程序的结果发现,对于日期对象可以直接排序,并且结果是正确的;
  2. 但是如果待排序元素是指 针,结果就不一定正确。(因为sort最终按照Less模板中方式比较,所以只会比较指针,而不是比较指针指 向空间中内容

此时可以使用类版本特化来处理上述问题:

// 对Less类模板按照指针方式特化
template<>
struct Less<Date*>
{
 bool operator()(Date* x, Date* y) const
 {
     return *x < *y;
 }
};
  • 特化之后,在运行上述代码,就可以得到正确的结果


(三)模板的分离编译

1、什么是模板分离编译?

  1. C++中的模板是一种通用的数据结构或算法,它们不是直接编译成目标代码,而是在实例化时根据类型参数生成相应的代码;
  2. 模板分离编译就是把模板的声明和定义分别放在.h和.cpp文件中,这样在编译时只需要编译一次模板,然后在链接时将所有实例化的代码链接起来。

2、 模板的分离编译

  •  首先,我先给大家明确一点的就是模板是不支持分离编译的,接下来,我简单的举例带大家感受一下。

现在有以下代码:

//func.cpp
#include"func.h"
template<class T>
T Add(const T& left, const T& right)
{
	return left + right;
}

void func()
{
	cout << "void func()" << endl;
}

//test.cpp
#include"func.h"
int main()
{
	Add(1, 2);
	Add(1.0, 2.0);

	return 0;
}


//func.h
#pragma once
#include<iostream>
#include<algorithm>
using namespace std;


template<class T>
T Add(const T& left, const T& right);

void func();

紧接着我们试着去编译看看结果如何:

但是此时我们定义的【func()】函数却可以进行编译 :

 分析如下:

3、 解决方法 

  • 1. 模板定义的位置显式实例化。这种方法不实用,不推荐使用

就像下面这样:

  • 但是这样去写的缺点也很明显,那就是先对十分的麻烦,极大的造成了

  • 2.将声明和定义放到一个文件 "xxx.hpp" 里面或者xxx.h其实也是可以的。推荐使用这种

  •  此时再去进行编译就可以正常的通过了


总结

到此,关于模板的讲解便到此结束了。接下来,我们简单的总结一下本文都学到了什么.

C++模板是一种灵活而强大的编程工具,它可以帮助我们实现泛型编程,即编写能够适用于多种数据类型的通用代码。

  1. C++模板分为函数模板和类模板两种。

  2. 函数模板是一种通用函数定义,可以接受不同类型的参数,并生成针对每个类型的特定函数定义。函数模板的定义以template关键字开头,后面跟着模板参数列表和函数定义。

  3. 类模板是一种通用类定义,可以接受不同类型的参数,并生成针对每个类型的特定类定义。类模板的定义以template关键字开头,后面跟着模板参数列表和类定义。

  4. 模板参数可以是类型参数、非类型参数或模板参数。类型参数指定了模板类型,非类型参数指定了模板值,而模板参数则允许我们将一个模板作为另一个模板的参数。

  5. 模板特化是指为特定类型或参数值提供特定的模板定义。可以通过显式特化和部分特化两种方式来完成模板特化。

  6. 模板库是C++标准库中的重要组成部分,包含了众多通用的模板类和函数,如STL容器、算法、迭代器等。

总之,C++模板是一种强大而灵活的编程工具,可以帮助我们实现泛型编程,并生成高效、通用的代码。对于C++程序员来说,掌握模板机制是必不可少的技能之一。

以上便是本文的全部内容,感谢各位的观看!!!

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

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

相关文章

Splashtop 推出首款专门面向创作者和创意工作室的高性能远程软件

2023年5月3日 加利福尼亚州库比蒂诺 Splashtop 在简化随处办公的远程解决方案领域处于领先地位&#xff0c;公司今天宣布推出 Splashtop Business Access Performance&#xff0c;这是一款全新的远程访问解决方案&#xff0c;针对独立艺术家、游戏玩家、建筑与设计以及创意公司…

Opencv项目实战:基于dlib的疲劳检测

文章目录 一、项目简介二、算法原理三、环境配置3.1、dlib人脸检测器&#xff1a;dlib.get_frontal_face_detector()3.2、dlib关键点定位工具&#xff1a;shape_predictor_68_face_landmarks.dat 四、项目实战&#xff08;加载视频&#xff09;五、项目实战&#xff08;摄像头获…

软件测试猿和bug的爱恨情仇

对程序猿来说改bug可以位列开发过程中最讨厌的事之首了&#xff0c;这么讲应该没有人会反对吧&#xff1f;因为就连Java之父詹姆斯高斯林也很讨厌Bug。 另一方面对于测试猿来说工作职责就是尽可能多地找出bug&#xff0c;并确保其得以解决。所以被程序猿视为眼中钉肉中刺的bug可…

Doris半结构化数据分析(倒排索引使用)快速入门

1. 背景 Apache Doris2.0 基于Apache Doris向量化MPP引擎&#xff0c;增加了倒排索引和半结构化JSON数据支持&#xff0c;更好地满足日志存储、检索、分析需求。与基于ES的日志存储方案相比&#xff0c;有如下优势&#xff1a; 性价比提升&#xff1a;存储成本降低50%以上&am…

‍☠️stm32Cubemx欠采样原理讲解与实现 采集高频信号

&#x1f3f4;‍☠️STM32Cubemx ADCTIMDMA欠采样采集高频信号 本文主要讲解ADC借助欠采样采集高频信号&#xff0c;比如使用100k左右的采样率去采集1M的信号。 所需工具&#xff1a; 开发板:STM32F103RCT6STM32CubeMXIDE: Keil-MDK 相关文章&#xff1a; STM32HAL ADCTIM…

2核4G轻量服务器阿里云和腾讯云区别对比

阿里云轻量应用服务器2核4G4M带宽297.98元12个月&#xff0c;腾讯云轻量2核4G5M服务器168元一年&#xff0c;628元3年&#xff0c;2核4G轻量应用服务器阿里云和腾讯云怎么选择&#xff1f;哪个性能比较好&#xff1f;阿腾云分享轻量应用服务器2核4G配置阿里云和腾讯云CPU、带宽…

打破互联网思维,我们该如何思考?

在会议上&#xff0c;你是否流程规范讲到一半突然卡逻辑、测分会议疯狂输出周围却一脸问号&#xff1f;提交缺陷时&#xff0c;你又是否被告知看不懂&#xff0c;要求补充信息&#xff1f;受挫时捶胸顿足“表达可太太太重要了&#xff0c;难道我天生脑子转的就比别人慢&#xf…

基于深度学习的高精度红外行人车辆检测识别系统(PyTorch+Pyside6+YOLOv5模型)

摘要&#xff1a;基于深度学习的高精度红外行人车辆检测识别系统可用于日常生活中或野外来检测与定位红外行人车辆目标&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的红外行人车辆目标检测识别&#xff0c;另外支持结果可视化与图片或视频检测结果的导出。本系…

围绕中国旅行商问题完成综合性实验报告 旅行商问题

题目&#xff1a;围绕中国旅行商问题完成综合性实验报告 旅行商问题&#xff08;TSP问题&#xff09;。 假设有一个旅行商人要拜访全国31个省会城市&#xff0c;它需要选择所要走的路径&#xff0c;路径的限制是每个城市只能拜访一次&#xff0c;而且最后要回到原来出发的城市。…

【裸机驱动LED】使用C代码驱动LED(三)—— C代码编写篇

前面只使用了汇编代码来驱动LED&#xff0c;但是对于后续一些比较复杂的逻辑&#xff0c;使用汇编代码编写驱动的难度太大&#xff0c;因此&#xff0c;这次我们要使用C语言代码来驱动LED。 除了C代码外&#xff0c;依然需要编写汇编代码&#xff0c;在没有OS的情况下&#xf…

基于黄金莱维引导机制的阿基米德优化算法(MSAOA)-附代码

基于黄金莱维引导机制的阿基米德优化算法(MSAOA) 文章目录 基于黄金莱维引导机制的阿基米德优化算法(MSAOA)1.阿基米德优化算法2. 改进阿基米德优化算法2.1 变区间初始化策略2.2 黄金莱维引导机制2.3 自适应波长算子 3.实验结果4.参考文献5.Matlab代码6.Python代码 摘要&#x…

什么是接口测试?怎么做接口测试?Apifox 教你做!

目录 前言&#xff1a; 一、什么是接口测试&#xff1f; 二、接口测试的步骤 三、接口测试工具的选择 四、总结 前言&#xff1a; 随着互联网和移动互联网的发展&#xff0c;企业面对着越来越庞大和复杂的系统和数据接口。在这种情况下&#xff0c;手动测试不再能够满足测…

修复uproject右键菜单完全解决方案办法

在你电脑中找到UnrealVersionSelector通常和epiclauncher是同一个根目录 epiclauncher路径是:D:\MyEpic\Epic Games\Launcher\Portal\Binaries\Win32\EpicGamesLauncher.exe UnrealVersionSelector.exe的路径是:D:\\MyEpic\\Epic Games\\Launcher\\Engine\\Binaries\\Win64\\Un…

探索新科技:3DVR电子楼书引领视觉体验革命

导语&#xff1a; 近年来&#xff0c;科技的迅猛发展引领着我们进入一个全新的数字化时代。在这个时代&#xff0c;虚拟现实和电子书成为了许多领域的热门话题。 接下来&#xff0c;让我们一起探索一个引人注目的技术创新&#xff0c;将虚拟现实和电子书完美结合的3DVR电子楼书…

python基本语法知识(二)

杂项 如果一个制表符/t不能对齐&#xff0c;可以多加几个&#xff1b; 只有将字典转换为字符串的时候才会保留字典的value&#xff0c;转成集合、列表、元组、都会丢失value 函数 例子1&#xff1a; str1 "hello world" # 函数定义 def my_len(data):count 0f…

基于 Ray 的大规模离线推理

本文整理自字节跳动基础架构资深研发工程师王万兴在火山引擎开发者社区 Meetup 中的分享。大模型离线推理&#xff0c;是指在具有数十亿或数千亿参数的大规模模型上进行分布式推理的过程。相较于常规模型推理&#xff0c;在模型切分、数据处理和数据流、提升 GPU 利用率方面面临…

ROCK PI S音频开发(一)系统准备

1、连接WIFI sudo nmcli r wifi on sudo nmcli dev wifi sudo nmcli dev wifi connect "SSID" password "PASSWORD" 2、更新源 sudo apt-get update sudo apt-get install git wget export DISTROfocal-stable wget -O - apt.radxa.com/$DISTRO/publ…

PHP快速实战20-PHP7中的垃圾回收机制与原理讲解

文章目录 前言PHP垃圾回收实现的原理垃圾回收机制引用计数循环垃圾收集 实现原理 总结 前言 本文已收录于PHP全栈系列专栏&#xff1a;PHP快速入门与实战 在计算机程序中&#xff0c;垃圾回收指的是一种自动管理内存的技术。在程序执行过程中&#xff0c;分配给它的内存会随着…

2023年软件测试趋势?测试人的发展前景?“我“到底该如何走...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 现在开始学习软件…

NetSuite 中国财务常用报表功能包

目录 1.致谢 2.功能说明 2.1 概述 2.2 报表说明 3.安装 4.操作指南 4.1 CLR_资产负债表 4.2 CLR_资产负债表&#xff08;期初/发生/结余&#xff09; 4.3 CLR_利润表 4.4 CLR_利润表季报 4.5 CLR_现金流量表 4.6 CLR_现金流量表季报 4.7 CLR_总账 4.8 CLR_序时账…