C++模板基础及代码实战

C++模板基础及代码实战

C++ 模板概览

泛型编程的支持

C++ 不仅为面向对象编程提供了语言支持,还支持泛型编程。正如第6章《设计可重用性》中讨论的,泛型编程的目标是编写可重用的代码。C++ 中支持泛型编程的基本工具是模板。虽然模板不严格是面向对象的特性,但它们可以与面向对象编程结合产生强大的效果。

模板的核心
  • 在过程化编程范式中,主要编程单元是过程或函数。函数之所以有用,主要是因为它们允许你编写与特定值无关的算法,因此可以用于许多不同的值。例如,C++ 中的 sqrt() 函数计算调用者提供的值的平方根。只计算一个数字(如四)的平方根的函数不会特别有用!sqrt() 函数是针对一个参数编写的,该参数是调用者传递的任何值的代表。
  • 对象导向编程范式增加了对象的概念,对象将相关数据和行为组合在一起,但它并没有改变函数和方法参数化值的方式。
模板的进阶参数化

模板将参数化概念进一步扩展,允许你对类型以及值进行参数化。C++ 中的类型包括 intdouble 等基本类型,以及 SpreadsheetCellCherryTree 等用户定义的类。有了模板,你可以编写不仅与它将要给定的值无关,而且与这些值的类型无关的代码。例如,你可以编写一个堆栈类定义,而不是编写用于存储 intCarsSpreadsheetCells 的单独堆栈类,这个堆栈类定义可以用于任何这些类型。

模板的使用和重要性

尽管模板是一项惊人的语言特性,但 C++ 中的模板在语法上可能令人困惑,许多程序员避免自己编写模板。然而,每个程序员至少需要知道如何使用模板,因为它们被广泛用于库,例如 C++ 标准库。本章教你如何在 C++ 中支持模板,重点是在标准库中出现的方面。在此过程中,你将了解一些除了使用标准库之外,你可以在程序中运用的巧妙特性。

类模板

类模板的定义和应用

类模板定义了一个类,其中一些变量的类型、方法的返回类型和/或方法的参数被指定为模板参数。类模板主要用于容器,即存储对象的数据结构。这一节通过运行示例 Grid 容器来说明。为了保持示例的合理长度并足够简单以阐明特定要点,本章的不同部分将为 Grid 容器添加不在后续部分使用的功能。

编写类模板

假设你想要一个通用的游戏棋盘类,可以用作国际象棋棋盘、跳棋棋盘、井字棋棋盘或任何其他二维游戏棋盘。为了使其具有通用性,你应该能够存储国际象棋棋子、跳棋棋子、井字棋棋子或任何类型的游戏棋子。

不使用模板编写代码

通过多态性建立通用游戏棋盘

在没有模板的情况下,构建通用游戏棋盘的最佳方法是使用多态性来存储通用的 GamePiece 对象。然后,你可以让每个游戏的棋子从 GamePiece 类继承。例如,在国际象棋游戏中,ChessPiece 将是 GamePiece 的派生类。通过多态性,编写为存储 GamePieceGameBoard 也可以存储 ChessPiece。因为可能需要复制 GameBoard,所以 GameBoard 需要能够复制 GamePiece。这种实现使用多态性,所以一种解决方案是在 GamePiece 基类中添加一个纯虚拟的 clone() 方法,派生类必须实现它以返回具体 GamePiece 的副本。

这是基本的 GamePiece 接口:

export class GamePiece {
public:virtual ~GamePiece() = default;virtual std::unique_ptr<GamePiece> clone() const = 0;
};

GamePiece 是一个抽象基类。具体类,如 ChessPiece,从它派生并实现 clone() 方法:

class ChessPiece : public GamePiece {
public:std::unique_ptr<GamePiece> clone() const override {// 调用复制构造函数来复制这个实例return std::make_unique<ChessPiece>(*this);}
};
GameBoard 的实现

GameBoard 的实现使用向量的向量和 unique_ptr 来存储 GamePieces

GameBoard::GameBoard(size_t width, size_t height) : m_width { width }, m_height { height } {m_cells.resize(m_width);for (auto& column : m_cells) {column.resize(m_height);}
}GameBoard::GameBoard(const GameBoard& src) : GameBoard { src.m_width, src.m_height } {// The ctor-initializer of this constructor delegates first to the// non-copy constructor to allocate the proper amount of memory.// The next step is to copy the data.for (size_t i { 0 }; i < m_width; i++) {for (size_t j { 0 }; j < m_height; j++) {if (src.m_cells[i][j]) {m_cells[i][j] = src.m_cells[i][j]->clone();}}}
}void GameBoard::verifyCoordinate(size_t x, size_t y) const {if (x >= m_width) {throw out_of_range { format("{} must be less than {}.", x, m_width) };}if (y >= m_height) {throw out_of_range { format("{} must be less than {}.", y, m_height) };}
}void GameBoard::swap(GameBoard& other) noexcept {std::swap(m_width, other.m_width);std::swap(m_height, other.m_height);std::swap(m_cells, other.m_cells);
}void swap(GameBoard& first, GameBoard& second) noexcept {first.swap(second);
}GameBoard& GameBoard::operator=(const GameBoard& rhs) {// Copy-and-swap idiomGameBoard temp { rhs }; // Do all the work in a temporary instance.swap(temp); // Commit the work with only non-throwing operations.return *this;
}const unique_ptr<GamePiece>& GameBoard::at(size_t x, size_t y) const {verifyCoordinate(x, y);return m_cells[x][y];
}unique_ptr<GamePiece>& GameBoard::at(size_t x, size_t y) {return const_cast<unique_ptr<GamePiece>&>(as_const(*this).at(x, y));
}

在这个实现中,at() 返回指定位置的棋子的引用,而不是棋子的副本。GameBoard 作为二维数组的抽象,应该通过给出索引处的实际对象而不是对象的副本来提供数组访问语义。

注意事项
  • 这个实现为 at() 提供了两个版本,一个返回非常量引用,另一个返回常量引用。
  • 使用复制和交换习语(copy-and-swap idiom)用于赋值运算符,以及 Scott Meyer 的 const_cast() 模式来避免代码重复。
GameBoard 类的使用
GameBoard chessBoard { 8, 8 };
auto pawn { std::make_unique<ChessPiece>() };
chessBoard.at(0, 0) = std::move(pawn);
chessBoard.at(0, 1) = std::make_unique<ChessPiece>();
chessBoard.at(0, 1) = nullptr;

这个 GameBoard 类运行得相当好,它可以用于国际象棋棋盘的创建和棋子的放置。

类模板实现的 Grid

GameBoard 的局限性

在上一节中的 GameBoard 类虽然实用,但有其局限性。首先,你无法使用 GameBoard 来按值存储元素;它总是存储指针。更严重的问题与类型安全有关。GameBoard 中的每个单元格都存储一个 unique_ptr<GamePiece>。即使你存储的是 ChessPieces,当你使用 at() 请求特定单元格时,你将得到一个 unique_ptr<GamePiece>。这意味着你必须将检索到的 GamePiece 向下转型为 ChessPiece 才能使用 ChessPiece 的特定功能。GameBoard 的另一个缺点是它不能用于存储原始类型,如 intdouble,因为单元格中存储的类型必须派生自 GamePiece

实现通用 Grid

因此,如果你能编写一个通用的 Grid 类来存储 ChessPiecesSpreadsheetCellsintsdoubles 等就很好了。在 C++ 中,你可以通过编写类模板来实现这一点,这允许你编写一个不指定一个或多个类型的类。然后客户端通过指定他们想要使用的类型来实例化模板。这就是所谓的泛型编程。

泛型编程的优势

泛型编程的最大优势是类型安全。类及其方法中使用的类型是具体类型,而不是像上一节中多态解决方案那样的抽象基类类型。例如,假设不仅有 ChessPiece,还有 TicTacToePiece

class TicTacToePiece : public GamePiece {
public:std::unique_ptr<GamePiece> clone() const override {// 调用复制构造函数来复制这个实例return std::make_unique<TicTacToePiece>(*this);}
};

使用上一节中的多态解决方案,没有什么能阻止你在同一个棋盘上存储井字棋棋子和国际象棋棋子:

GameBoard chessBoard { 8, 8 };
chessBoard.at(0, 0) = std::make_unique<ChessPiece>();
chessBoard.at(0, 1) = std::make_unique<TicTacToePiece>();

这样做的一个大问题是,你需要记住一个单元格存储了什么,以便在调用 at() 时执行正确的向下转型。

Grid 类模板的定义

类模板的语法

为了理解类模板,检查其语法非常有帮助。以下示例展示了如何将 GameBoard 类修改为模板化的 Grid 类。代码后面会详细解释语法。请注意,类名已从 GameBoard 改为 Grid

使用值语义实现 Grid

GameBoard 实现中使用的多态指针语义相比,我选择使用值语义而不是多态来实现这个解决方案。m_cells 容器存储实际对象,而不是指针。与指针语义相比,使用值语义的一个缺点是不能有真正的空单元格;也就是说,单元格必须始终包含某个值。使用指针语义时,空单元格存储 nullptrstd::optional 在这里提供了帮助。它允许你在仍然有表示空单元格的方法的同时使用值语义。

template <typename T>
class Grid {
public:explicit Grid(size_t width = DefaultWidth, size_t height = DefaultHeight);virtual ~Grid() = default;// Explicitly default a copy constructor and assignment operator.Grid(const Grid& src) = default;Grid& operator=(const Grid& rhs) = default;// Explicitly default a move constructor and assignment operator.Grid(Grid&& src) = default;Grid& operator=(Grid&& rhs) = default;std::optional<T>& at(size_t x, size_t y);const std::optional<T>& at(size_t x, size_t y) const;size_t getHeight() const { return m_height; }size_t getWidth() const { return m_width; }static const size_t DefaultWidth { 10 };static const size_t DefaultHeight { 10 };private:void verifyCoordinate(size_t x, size_t y) const;std::vector<std::vector<std::optional<T>>> m_cells;size_t m_width { 0 }, m_height { 0 };
};
类模板的详细解读
  • export template <typename T>:这一行表示接下来的类定义是一个关于类型 T 的模板,并且它正在从模块中导出。templatetypename 是 C++ 中的关键字。如前所述,模板在类型上“参数化”,就像函数在值上“参数化”一样。
  • 使用模板类型参数名(如 T)来表示调用者将作为模板类型参数传递的类型。T 的名称没有特殊含义——你可以使用任何你想要的名称。
关于模板类型参数的注意事项
  • 出于历史原因,你可以使用关键字 class 而不是 typename 来指定模板类型参数。因此,许多书籍和现有程序使用类似 template <class T> 的语法。然而,在这种情况下使用 class 这个词是令人困惑的,因为它暗示类型必须是一个类,这实际上并不正确。类型可以是类、结构体、联合、语言的原始类型,如 intdouble 等。

Grid 类模板与 GameBoard 类的对比

数据成员的变化

在之前的 GameBoard 类中,m_cells 数据成员是指针的向量的向量,这需要特殊的复制代码,因此需要拷贝构造函数和拷贝赋值操作符。在 Grid 类中,m_cells 是可选值的向量的向量,所以编译器生成的拷贝构造函数和赋值操作符是可以的。

显式默认构造函数和操作符

一旦你有了用户声明的析构函数,就不推荐编译器隐式生成拷贝构造函数或拷贝赋值操作符,因此 Grid 类模板显式地将它们默认化。它还显式默认化了移动构造函数和移动赋值操作符。以下是显式默认的拷贝赋值操作符:

Grid& operator=(const Grid& rhs) = default;

可以看到,rhs 参数的类型不再是 const GameBoard&,而是 const Grid&。在类定义内,编译器会在需要时将 Grid 解释为 Grid<T>,但如果你愿意,也可以显式地使用 Grid<T>

Grid<T>& operator=(const Grid<T>& rhs) = default;

然而,在类定义外,你必须使用 Grid<T>。当你编写类模板时,你过去认为的类名(Grid)实际上是模板名。当你想谈论实际的 Grid 类或类型时,你必须使用模板 ID,即 Grid<T>,这些是针对特定类型(如 intSpreadsheetCellChessPiece)的 Grid 类模板的实例化。

at() 方法的更新

由于 m_cells 不再存储指针,而是存储可选值,at() 方法现在返回 std::optional<T> 而不是 unique_ptrs,即可以有类型 T 的值,也可以为空的 optionals

std::optional<T>& at(size_t x, size_t y);
const std::optional<T>& at(size_t x, size_t y) const;

Grid 类模板的方法定义

模板方法定义格式

每个 Grid 模板的方法定义都必须以 template <typename T> 说明符开头。构造函数如下所示:

template <typename T>
Grid<T>::Grid(size_t width, size_t height) : m_width { width }, m_height { height } {m_cells.resize(m_width);for (auto& column : m_cells) {column.resize(m_height);}
}

注意:类模板的方法定义需要对使用该类模板的任何客户端代码可见。这对方法定义的位置施加了一些限制。通常,它们直接放在类模板定义本身的同一文件中。本章后面讨论了绕过这一限制的一些方法。

类名和方法定义

请注意,:: 前的类名是 Grid<T>,而不是 Grid。在所有方法和静态数据成员定义中,你必须指定 Grid<T> 作为类名。构造函数的主体与 GameBoard 构造函数相同。其他方法定义也类似于 GameBoard 类中的对应方法,但有适当的模板和 Grid<T> 语法变化:

template <typename T>
void Grid<T>::verifyCoordinate(size_t x, size_t y) const {if (x >= m_width) {throw std::out_of_range { std::format("{} must be less than {}.", x, m_width) };}if (y >= m_height) {throw std::out_of_range { std::format("{} must be less than {}.", y, m_height) };}
}template <typename T>
const std::optional<T>& Grid<T>::at(size_t x, size_t y) const {verifyCoordinate(x, y);return m_cells[x][y];
}template <typename T>
std::optional<T>& Grid<T>::at(size_t x, size_t y) {return const_cast<std::optional<T>&>(std::as_const(*this).at(x, y));
}
类模板方法的默认值

注意:如果类模板方法的实现需要某个模板类型参数的默认值(例如 T),则可以使用 T{} 语法。T{} 调用对象的默认构造函数(如果 T 是类类型),或生成零(如果 T 是基本类型)。这种语法称为零初始化语法。它是为尚不知道类型的变量提供合理默认值的好方法。

使用 Grid 类模板

模板实例化

当你想要创建 Grid 对象时,不能单独使用 Grid 作为类型;你必须指定将存储在该 Grid 中的类型。为特定类型创建类模板对象称为实例化模板。以下是一个示例:

Grid<int> myIntGrid; // 声明一个存储 int 的网格,使用构造函数的默认参数。
Grid<double> myDoubleGrid { 11, 11 }; // 声明一个 11x11 的 double 类型网格。
myIntGrid.at(0, 0) = 10;
int x { myIntGrid.at(0, 0).value_or(0) };
Grid<int> grid2 { myIntGrid }; // 拷贝构造函数
Grid<int> anotherIntGrid;
anotherIntGrid = grid2; // 赋值操作符

请注意 myIntGridgrid2anotherIntGrid 的类型是 Grid<int>。你不能在这些网格中存储 SpreadsheetCellsChessPieces;如果尝试这样做,编译器将生成错误。

使用 value_or()

还要注意 value_or() 的使用。at() 方法返回一个可选引用,可能包含值也可能不包含。value_or() 方法在可选项中有值时返回该值;否则,它返回给 value_or() 的参数。

模板类型的重要性

模板类型的指定非常重要;以下两行都无法编译:

Grid test; // 无法编译
Grid<> test; // 无法编译

如果你想声明一个接受 Grid 对象的函数或方法,你必须在 Grid 类型中指定存储在网格中的类型:

void processIntGrid(Grid<int>& grid) { /* 省略正文以简洁 */ }

或者,你可以使用本章后面讨论的函数模板,编写一个模板化的函数,该函数根据网格中元素的类型进行模板化。

注意:你可以使用类型别名来简化完整的 Grid 类型的重复书写,例如 Grid<int>

using IntGrid = Grid<int>;
void processIntGrid(IntGrid& grid) { /* 正文 */ }
Grid 类模板的多样性

Grid 类模板可以存储的不仅仅是 int。例如,你可以实例化一个存储 SpreadsheetCellsGrid

Grid<SpreadsheetCell> mySpreadsheet;
SpreadsheetCell myCell { 1.234 };
mySpreadsheet.at(3, 4) = myCell;

你也可以存储指针类型:

Grid<const char*> myStringGrid;
myStringGrid.at(2, 2) = "hello";

指定的类型甚至可以是另一个模板类型:

Grid<vector<int>> gridOfVectors;
vector<int> myVector { 1, 2, 3, 4 };
gridOfVectors.at(5, 6) = myVector;

你还可以在自由存储区动态分配 Grid 模板实例:

auto myGridOnFreeStore { make_unique<Grid<int>>(2, 2) }; // 自由存储区上的 2x2 网格。
myGridOnFreeStore->at(0, 0) = 10;
int x { myGridOnFreeStore->at(0, 0).value_or(0) };

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

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

相关文章

C/C++---------------LeetCode第350. 两个数组的交集 II

两个数组的交集|| 题目及要求双指针哈希表在main内使用 题目及要求 给你两个整数数组 nums1 和 nums2 &#xff0c;请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数&#xff0c;应与元素在两个数组中都出现的次数一致&#xff08;如果出现次数不一致&#xff0…

基于springboot + vue大学生竞赛管理系统

qq&#xff08;2829419543&#xff09;获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;springboot 前端&#xff1a;采用vue技术开发 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xf…

基于单片机的智能健康监测手环的设计

目 录 1 绪论... 2 1.1 引言... 2 1.2 智能手环的国内外研究现状... 2 1.3 课题的研究意义... 3 1.4 本文的研究内容和章节安排... 4 2 智能手环系统设计方案... 5 2.1 系统总体设计方案... 5 2.2 主芯片选择... 5 2.3 显示方案的选择... 6 2.4 倾角传感器的选择... 6 2.5 心率…

【AIGC】AI作图最全提示词prompt集合(收藏级)

目录 一、正向和负向提示词 二、作图参数 你好&#xff0c;我是giszz. AI做图真是太爽了&#xff0c;解放生产力&#xff0c;发展生产力。 但是&#xff0c;你是不是也总疑惑&#xff0c;为什么别人的图&#xff0c;表现力那么丰富呢&#xff0c;而且指哪打哪&#xff0c;要…

DFT(离散傅里叶变换)的通俗理解

本文包含了博主对离散傅里叶变换&#xff0c;负频率&#xff0c;实信号与复信号频谱的理解&#xff0c;如有不妥&#xff0c;欢迎各位批评指正与讨论。 文章目录 DFT的理解信号的频谱实信号的频谱复信号的频谱 DFT的理解 傅里叶变换是一种将信号从时域转换到频域的数学工具。…

SQL Server事务(Transaction)

5. 事务(Transaction) 5.1. 事务概念 事务是关系库中不可分割的一系列数据库操作,这些操作必须要么整体成功,要么整体失败。事务维护数据完整性,保证数据库总是处于一致性状态。虽然,各关系库中事务实现和操作的具体细节有所不同,但基本概念和功能完全相同,而具体操作…

通信标准化协会,信通院及量子信息网络产业联盟调研玻色量子,共绘实用化量子未来!

8月14日&#xff0c;中国通信标准化协会&#xff0c;信通院标准所及量子信息网络产业联盟等单位领导走访调研北京玻色量子科技有限公司&#xff08;以下简称“玻色量子”&#xff09;&#xff0c;参观了玻色量子公司及自建的十万颗粒洁净度的光量子信息技术实验室&#x1f517;…

【win32_003】不同字符集下的通用字符串语法TCHAR、TEXT、PTSTR、PCTSTR

TCHAR 通用 根据项目属性是否使用Unicode字符集&#xff0c;TCHAR被解释为CHAR(char)或WCHAR(wchar_t)数据类型。 TCHAR a ‘A’ ; TCHAR arr [] TEXT(“AA”); TCHAR arr [100] TEXT(“AA”); TCHAR *pstr TEXT(“AA”); TEXT宏 #ifdef UNICODE #define __TEXT(quote) L#…

STM32下载程序的五种方法

刚开始学习 STM32 的时候&#xff0c;很多小伙伴满怀热情买好了各种设备&#xff0c;但很快就遇到了第一个拦路虎——如何将写好的代码烧进去这个黑乎乎的芯片&#xff5e; STM32 的烧录方式多样且灵活&#xff0c;可以根据实际需求选择适合的方式来将程序烧录到芯片中。本文将…

10年前,我就用 SQL注入方式发现了学校网站的漏洞

大家好&#xff0c;我是风筝。 事情是这样子的&#xff0c;在10年以前&#xff0c;某个月黑风高夜的夜里&#xff0c;虽然这么说有点暴露年龄了&#xff0c;但无所谓&#xff0c;毕竟我也才18而已。我打开电脑&#xff0c;在浏览器中输入我们高中学校的网址&#xff0c;页面很…

TCP首部格式_基本知识

TCP首部格式 表格索引: 源端口目的端口 序号 确认号 数据偏移保留 ACK等 窗口检验和紧急指针 TCP报文段首部格式图 源端口与目的端口: 各占16位 序号:占32比特&#xff0c;取值范围0~232-1。当序号增加到最后一个时&#xff0c;下一个序号又回到0。用来指出本TCP报文段数据载…

【win32_004】字符串处理函数

StringCbPrintf 函数 (strsafe.h)&#xff1a;格式化字符串 STRSAFEAPI StringCbPrintf([out] STRSAFE_LPSTR pszDest,//目的缓冲区 LPSTR指针或者数组[in] size_t cbDest,//目的缓冲区大小[in] STRSAFE_LPCSTR pszFormat,//格式 例如&#xff1a; TEXT("%d&…

ctfhub技能树_web_信息泄露

目录 二、信息泄露 2.1、目录遍历 2.2、Phpinfo 2.3、备份文件下载 2.3.1、网站源码 2.3.2、bak文件 2.3.3、vim缓存 2.3.4、.DS_Store 2.4、Git泄露 2.4.1、log 2.4.2、stash 2.4.3、index 2.5、SVN泄露 2.6、HG泄露 二、信息泄露 2.1、目录遍历 注&#xff1…

【ArcGIS Pro微课1000例】0050:如何清除坐标系信息

文章目录 一、目的二、方法1. 使用【定义投影】工具2. 清除数据的投影信息3. 删除坐标文件 一、目的 地理信息数据的坐标系是将地理信息数据进行融合、叠加、分析的重要数学框架&#xff0c;而其描述信息是非常重要的元数据&#xff0c;涉及整个国家的测绘坐标系统&#xff0c…

【CSP】202309-1_坐标变换(其一)Python实现

文章目录 [toc]试题编号试题名称时间限制内存限制问题描述输入格式输出格式样例输入样例输出样例说明评测用例规模与约定Python实现 试题编号 202309-1 试题名称 坐标变换&#xff08;其一&#xff09; 时间限制 1.0s 内存限制 512.0MB 问题描述 对于平面直角坐标系上的坐标 (…

DSSS技术和OFDM技术

本内容为学习笔记&#xff0c;内容不一定正确&#xff0c;请多处参考进行理解 https://zhuanlan.zhihu.com/p/636853588 https://baike.baidu.com/item/OFDM/5790826?frge_ala https://zhuanlan.zhihu.com/p/515701960?utm_id0 一、 DSSS技术 信号替代&#xff1a;DSSS技术为…

python中列表的方法

文章目录 列表的方法sort()append() 列表的方法 列表&#xff08;List&#xff09;是Python中的一种数据结构&#xff0c; 提供了一些可以操作列表的方法。以下是一些常见的列表方法&#xff1a; append()&#xff1a;向列表末尾添加一个元素。 my_list [1, 2, 3] my_list.…

C++ 操作MinIO做文件数据的上传和下载(踩坑与经验)包含编译包

前言 最近在做项目流程优化&#xff0c;准备将之前的java对文件的操作转换到c端&#xff0c;因此做了基于c的minio操作的测试demo。期间的各种踩坑与问题&#xff0c;花了一天时间总算是成功了&#xff0c;当然还有一些小问题&#xff0c;等待后续其他大拿解决。 项目环境 v…

Jmeter调用本地Exe程序

背景&#xff1a; 候使用C#编译的小工具制作压测的请求的入参&#xff0c;因Jmeter无法调用C#的方法&#xff0c;可以把C#编译个exe程序&#xff0c;在启动压测前&#xff0c;让JMeter调用本地的exe批量生成压测数据。 使用步骤&#xff1a; 打开Jmeter&#xff0c;右击选择…

【Vue】使用cmd命令创建vue项目

上一篇&#xff1a; node的安装与配置 https://blog.csdn.net/m0_67930426/article/details/134562278?spm1001.2014.3001.5502 目录 一.创建空文件夹专门存放vue项目 二. 查看node , npm 和vue脚手架的版本 三.安装vue脚手架 四.创建vue项目 五.运行项目 一.创建空文件…