[C++随笔录] string模拟实现

news/2024/2/27 22:25:26

string模拟实现

  • 基本结构
  • 天选之子
    • 构造函数
    • 析构函数
    • 拷贝构造函数
  • 空间
    • size()函数
    • capacity()函数
    • clear()函数
    • empty()函数
    • reverse()函数
    • resize()函数
  • 迭代器
    • iterator
      • begin()函数
      • end()函数
    • const_iterator
      • begin()函数
      • end()函数
    • push_back()函数
    • append()函数
    • operator+=
    • insert()函数
    • erase()函数
    • find()函数
    • swap()函数
    • operator[]函数
    • operator= 函数
  • 比较
  • 流操作
    • 流插入 <<
    • 流提取 >>
  • C接口
    • c_str()函数
    • substr()函数
  • 源码

放在前面:
我们实现string类, 并不是跟源码一模一样(并不是造一个新的轮子), 只是了解每个接口的实现过程 ⇒ 我们以后也用的放心(比如时间复杂度, 空间复杂度等等)

基本结构

private:size_t _size; // 实际长度size_t _capacity; // 空间char* _str;

习惯把不可能为负数的值的类型 定义为 size_t

天选之子

构造函数

  1. 考虑到 无参调用和有参调用 && 只有一个参数 ⇒ 我们可以采用 全缺省的形式
  2. 传参类型应该为 常量字符串 ⇒ const char* ⇐ 一般用于初始化, 咋们给的值都是常量
  3. 缺省值初始化为 ""(空字符串) ⇐ 常量字符串默认就会有 \0, 即 “” (空字符串) 里面就是一个 \0
  4. _size 和 _capacity 的大小不包括 \0 ⇒ 所以, 我们初始化长度的时候, 用 strlen(str)
  5. _str要先开空间

👇👇👇

string(const char* str = ""):_size(strlen(str)),_capacity(_size),_str(new char[_capacity+1]){memcpy(_str, str, _capacity+1);}

注意:

  1. 初始化的顺序是按照 声明的顺序来的 ⇒ 我们尽量保持 初始化和声明的顺序一致, 要不然就会出现问题
  2. 由于 _size 和 _capacity不包括 \0 的长度 ⇒ 我们_str开空间的时候要多开一个, 给 \0

🗨️为啥要用 memcpy函数? 为啥不用strcpy函数呢?

  • 1. memcpy函数 和 strcpy函数的 区别 : memcpy函数是 逐个字节进行copy的, 而strcpy是 遇到 \0就停止 copy
    2 我们标准库中 string类的输出是按照 _size 来的. 即遇到下面的情况, strcpy 和 strcpy的区别就体现出来了👇👇👇
	// 字符串如下:// hello // world!// 来做以下几组实验// 1. 库里的stringstd::string str1 = "hello";str1 += "\0";str1 += "world!";cout << str1 << endl;// 2. 用strcpy来实现// ...//..// 3. 用memcpy来实现// ...// ...*****
1. helloworld!
2. hello
3. helloworld!
*****
  1. memcpy默认是不会copy \0, 所以memcpy函数里面的长度 传的是 _capacity+1

析构函数

~string()
{delete[] _str; // 清理动态申请空间// 置零(置空)_str = nullptr;_size = _capacity = 0;
}

拷贝构造函数

  1. 我们不写构造函数, 系统自动生成的是一种 浅拷贝 ⇒ 对有动态资源申请的对象来说, 会对同一块空间析构两次
  2. 我们写的是 深拷贝 ⇒ 找一块新的空间给 this->_str, 然后将 s的内容 copy过去, 更新 _capacity 和 _size
String(const string& s)
{_str = new char[s._capacity + 1];memcpy(_str, s._str, s._capacity + 1);_capacity = s._capacity;_size = s._size;
}

空间

size()函数

size_t size()const
{return _size;
}

capacity()函数

size_t capacity()const
{return _capacity;
}

clear()函数

void clear()
{_size = 0;_str[_size] = '\0';
}

clear()函数 并不是用来清理空间的, 而是让空间置空(置零)

empty()函数

bool empty()const 
{if(_size == 0)return true;return false;
}

reverse()函数

void reverse(size_t n)
{assert(n >= 0);// 扩容逻辑 -- 一般我们不进行缩容if(n > _capacity){char* tem = new char[n+1];memcpy(tem._str, _str, _capacity+1);delete[] _str;_str = tem;_capacity = n;}	
}

resize()函数

void resize(size_t n, char ch = '\0')
{assert(n >= 0);if(n <= _size){_size = n;_str[_size] = '\0';}else{reverse(n);for(int i = _size; i < _size+n; i++){_str[i] = ch;}_size = _size + n;_str[_size] = '\0';}
}

迭代器

迭代器是属于类的 ⇐ 我们声明迭代器的时候要声明类域
👇👇👇

std::string str = "hello world";
iterator it = str.begin();*****
error C2955: “std::iterator”: 使用 类 模板 需要 模板 参数列表
*****

但要在 string类里面,定义一种类型, 有两种方式:

  1. typedef 一个变量
  2. 定义一个内部类 (内部类一般都是自定义类型)

而我们这里iterator其实就是数组_str各个下标对应的地址, 是一种 内置类型 ⇒ 所以, 我们采用typedef的方式来实现 iterator

iterator

typedef char* iterator;

begin()函数

iterator begin()
{return _str;
}

end()函数

iterator end()
{return _str + _size;
}

const_iterator

typedef const char* const_iterator;

begin()函数

const_iterator begin()const
{return _str;
}

end()函数

const_iterator end()const
{return _str + _size;
}

push_back()函数

尾插一个字符的操作:

  1. 是否需要扩容 ⇐ _size == _capacity

  2. 扩容逻辑:

    1. _capacity == 0 ⇒ 传个 4 过去扩容
    2. _capacity > 0 ⇒ 2倍扩容
  3. _size++, _str[_size] = ‘\0’;

void push_back(const char ch)
{// 是否扩容if (_size == _capacity){size_t newcapacity = (_capacity == 0 ? 4 : _capacity * 2);reverse(newcapacity);}_str[_size] = ch;_size++;_str[_size] = '\0';}

append()函数

尾插一个字符串的操作:

  1. 是否需要扩容

  2. 扩容逻辑:
    1. _size + len <= _capacity — — 不需要扩容
    2. _size + len > _capacity — — 扩容(_size + len)

  3. _size = _size + len, _str[_size] = ‘\0’;

void append(const char* ch)
{size_t len = strlen(ch);// 是否扩容if (len + _size > _capacity){reverse(len + _size);}for (size_t i = 0; i < len; i++){_str[_size + i] = ch[i];}_size += len;_str[_size] = '\0';}

operator+=

复用 push_back() 和 append()

void operator+=(const char ch)
{push_back(ch);}void operator+=(const char* ch)
{append(ch);}

insert()函数

在 下标为pos的位置插入n个字符:

  1. 是否需要扩容

  2. 扩容逻辑:

    1. _size + n <= _capacity — — 不需要扩容
    2. _size + n > _capacity — — 扩容(_size + n)
  3. 挪动数据

  4. _size = _size + n, _str[_size] = ‘\0’;

void insert(size_t pos, const char* ch)
{assert(pos >= 0);// 是否需要扩容size_t len = strlen(ch);if (_size + len > _capacity){reverse(_size + pos);}// 挪动数据size_t end = _size;// 挪动数据时, 下标不能小于0(即不能等于 -1)while (end >= pos && end != _nops){_str[end + len] = _str[end];end--;}// 插入数据for (size_t i = 0; i < len; i++){_str[pos + i] = ch[i];}_size = _size + len;
}
  • 对了, 这里的 _nops是我么定义的一个静态成员变量
// 类里面的声明
public:static size_t _nops;// 类外面的初始化
size_t muyu::string::_nops = -1; // 这里的muyu是我定义的一个命名空间域

🗨️为啥要定义一个nops? 为啥要初始化为 -1?

  • 前面, 我们有说过: 不可能为负数的, 我们定义成 size_t (无符号整数)
    如果 下标减到 -1 — ---- 由于是 size_t, 变量是不会比 -1 小的
    那么 size_t 类型如何区分开 -1 呢?
    size_t i = -1; ⇒ i 等于 2 ^ 32 -1;
    那么 下标 不等于 nops不就行了~~
    还有就是, 插入函数 和 删除函数中 字符串的长度如果不写, 就是nops

erase()函数

void erase(size_t pos, size_t n = _nops)
{assert(pos >= 0);// 是否把pos位置后面全部删除if (n == _nops || pos + n >= _size){_str[pos] = '\0';_size = pos;}else{for (size_t i = pos; i < pos + n; i++){_str[i] = _str[i + n];}_size = _size - n;}
}

find()函数

size_t find(size_t pos = 0, const char ch )
{assert(pos < _size);for (int i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return _nops;
}size_t find(size_t pos = 0, const char* ch )
{assert(pos < _size);// 如果找到返回地址, 否则返回nullptrconst char* res = strstr(_str, ch);if (res){return res - _str;}else{return _nops;}}

swap()函数

void swap(string& s)
{std::swap(_size, s._size);std::swap(_capacity, s._capacity);std::swap(_str, s._str);
}

operator[]函数

//不是&, 那就返回的是常量临时拷贝
char& operator[](size_t n)
{assert(n <= _size);return _str[n];
}const char& operator[](size_t n)const 
{assert(n <= _size);return _str[n];
}

operator= 函数

//string& operator=(const string& s)
//{
//	// 传统写法 -- 找一块空间, 把s的内容搞过去, 然后和*this交换
//	// 1. 找空间, 移内容;  2. 释放this的空间//	string tem;
//	tem.reverse(s._capacity + 1);
//	memcpy(tem._str, s._str, s._capacity + 1);
//	tem._size = s._size;//	swap(tem);//	return *this;//}string& operator=(string s)
{swap(s);return *this;
}

比较

bool operator==(const string& s)
{// 如果_size都不相等, 那么何谈相等return _size == s._size &&memcmp(_str, s._str, _size) == 0;
}bool operator>(const string& s)
{// 取较小长度进行比较size_t size = std::min(_size, s._size);int ret = memcmp(_str, s._str, size);// 由于是取较小长度进行比较, 那么会出现如下几种情况:// 1. str1 = hello, str2 = hello// 2. str1 = hello\0xxx, str2 = hello// 3. str1 = hello, str2 = hello\00xxx// 这几种情况都是根据较小长度比较的结果都是 相等if (ret == 0){if (_size > s._size)return true;elsereturn false;}return ret > 0;}bool operator!=(const string& s)
{return !(*this == s);
}bool operator>=(const string& s)
{return *this == s || *this > s;
}bool operator<(const string& s)
{return !(*this >= s);
}bool operator<=(const string& s)
{return !(*this > s);
}

流操作

流操作要写在全局位置 ⇐ cout/cin 要抢占第一个参数. 若要是在类中, 第一个参数就默认是this

流插入 <<

ostream& operator<<(ostream& out, const string& s)
{for (auto ch : s){out << ch;}return out;
}

流提取 >>

istream& operator>>(istream& in, string& s)
{// 每一次新的读取要进行清理一下// 要不然就会接着读取, 而不是覆盖s.clear();// get()函数可以读到每一个字符, 包括空格 和 换行char ch = in.get();// 处理前缓冲区前面的空格或者换行while (ch == ' ' || ch == '\n'){ch = in.get();}// in >> ch;char buff[128]; // buff数组的作用是: 减少开空间的次数int i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){buff[i] = '\0';s += buff;i = 0;}//in >> ch;ch = in.get();}// 如果最后 buff数组还有数据, 那么就加到s中if (i != 0){buff[i] = '\0';s += buff;}return in;
}

C接口

c_str()函数

const char* c_str()const 
{return _str;
}

substr()函数

string substr(size_t pos = 0, size_t n = _nops)
{assert(pos >= 0);// 是否需要扩容int len = n; // 默认是nif (n == _nops || pos + n >= _size){len = _size - pos;}string tem;tem.reverse(len);//for (size_t i = pos; i < len; i++)//{//	tem[i] = _str[i + pos];//}//tem._size = len;//tem[_size] = '\0';for (size_t i = pos; i < pos + len; i++){tem += _str[i];}return tem;
}

源码

#pragma once#include <string.h>
#include<assert.h>
#include<iostream>namespace muyu
{class string{public:typedef char* iterator;typedef const char* const_iterator;// friend ostream& operator<<(ostream& out, const string& s);iterator begin(){return _str;}const_iterator begin()const{return _str;}iterator end(){return _str + _size;}const_iterator end()const{return _str + _size;}string(const char* str = ""):_size(strlen(str)),_capacity(_size),_str(new char[_capacity+1]){memcpy(_str, str, _capacity+1);}string(const string& tem){_str = new char[tem._capacity + 1];memcpy(_str, tem._str, tem._capacity + 1);_capacity = tem._capacity;_size = tem._size;}~string(){delete[] _str;_str = nullptr;_size = _capacity = 0;}const char* c_str()const{return _str;}void reverse(size_t n){if (n > _capacity){char* tem = new char[n + 1];memcpy(tem, _str, _capacity + 1);_capacity = n;delete[] _str;_str = tem;}}void resize(size_t n, char ch = '\0'){if (_size > n){_str[n] = '\0';_size = n;}else{reverse(n); // 不管需不需要扩容,都丢给reverse. reverse内部有判断是否需要扩容for (size_t i = _size; i < n; i++){_str[i] = ch;}_str[n] = '\0';}}void push_back(const char ch){// 是否扩容if (_size == _capacity){size_t newcapacity = (_capacity == 0 ? 4 : _capacity * 2);reverse(newcapacity);}_str[_size] = ch;_size++;_str[_size] = '\0';}void append(const char* ch){size_t len = strlen(ch);// 是否扩容if (len + _size > _capacity){reverse(len + _size);}for (size_t i = 0; i < len; i++){_str[_size + i] = ch[i];}_size += len;_str[_size] = '\0';}void operator+=(const char ch){push_back(ch);}void operator+=(const char* ch){append(ch);}void insert(size_t pos, const char* ch){assert(pos >= 0);// 是否需要扩容size_t len = strlen(ch);if (_size + len > _capacity){reverse(_size + pos);}// 挪动数据size_t end = _size;// 挪动数据时, 下标不能小于0(即不能等于 -1)while (end >= pos && end != _nops){_str[end + len] = _str[end];end--;}// 插入数据for (size_t i = 0; i < len; i++){_str[pos + i] = ch[i];}_size = _size + len;}void erase(size_t pos, size_t n = _nops){assert(pos >= 0);if (n == _nops || pos + n >= _size){_str[pos] = '\0';_size = pos;}else{for (size_t i = pos; i < pos + n; i++){_str[i] = _str[i + n];}_size = _size - n;}}size_t size()const{return _size;}void clear(){_size = 0;_str[_size] = '\0';}bool empty()const{return _size > 0;}void swap(string& s){std::swap(_size, s._size);std::swap(_capacity, s._capacity);std::swap(_str, s._str);}//不是&, 那就返回的是常量临时拷贝char& operator[](size_t n){assert(n <= _size);return _str[n];}const char& operator[](size_t n)const {assert(n <= _size);return _str[n];}string substr(size_t pos = 0, size_t n = _nops){assert(pos >= 0);int len = n; // 默认是nif (n == _nops || pos + n >= _size){len = _size - pos;}string tem;tem.reverse(len);//for (size_t i = pos; i < len; i++)//{//	tem[i] = _str[i + pos];//}//tem._size = len;//tem[_size] = '\0';for (size_t i = pos; i < pos + len; i++){tem += _str[i];}return tem;}bool operator==(const string& s){return _size == s._size &&memcmp(_str, s._str, _size) == 0;}bool operator>(const string& s){// 取较小长度进行比较size_t size = std::min(_size, s._size);int ret = memcmp(_str, s._str, size);if (ret == 0){if (_size > s._size)return true;elsereturn false;}return ret > 0;}bool operator!=(const string& s){return !(*this == s);}bool operator>=(const string& s){return *this == s || *this > s;}bool operator<(const string& s){return !(*this >= s);}bool operator<=(const string& s){return !(*this > s);}size_t find(const char ch, size_t pos = 0){assert(pos < _size);for (int i = pos; i < _size; i++){if (_str[i] == ch){return i;}}return _nops;}size_t find(const char* ch, size_t pos = 0){assert(pos < _size);const char* res = strstr(_str, ch);if (res){return res - _str;}else{return _nops;}}//string& operator=(const string& s)//{//	// 传统写法 -- 找一块空间, 把s的内容搞过去, 然后和*this交换//	// 1. 找空间, 移内容;  2. 释放this的空间//	//string tem;//	//tem.reverse(s._capacity + 1);//	//memcpy(tem._str, s._str, s._capacity + 1);//	//tem._size = s._size;//	//swap(tem);//	//return *this;//}string& operator=(string s){swap(s);return *this;}private:size_t _size;size_t _capacity;char* _str;public:static size_t _nops;};size_t string::_nops = -1;ostream& operator<<(ostream& out, const string& s){for (auto ch : s){out << ch;}return out;}istream& operator>>(istream& in, string& s){// 每一次新的读取要进行清理一下// 要不然就会接着读取, 而不是覆盖s.clear();// get()函数可以读到每一个字符, 包括空格 和 换行char ch = in.get();// 处理前缓冲区前面的空格或者换行while (ch == ' ' || ch == '\n'){ch = in.get();}// in >> ch;char buff[128]; // buff数组的作用是: 减少开空间的次数int i = 0;while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == 127){buff[i] = '\0';s += buff;i = 0;}//in >> ch;ch = in.get();}// 如果最后 buff数组还有数据, 那么就加到s中if (i != 0){buff[i] = '\0';s += buff;}return in;}}

持志如心痛. — — 王阳明
译:心在痛上,岂有工夫说闲话、管闲事.


http://www.ppmy.cn/news/1115985.html

相关文章

中期科技:智慧公厕是智慧城市管理智慧化的至佳表现

智慧公厕&#xff0c;作为智慧城市建设的一部分&#xff0c;正逐渐成为城市管理的新标杆。它不仅为城市居民提供便利&#xff0c;还深刻地改善了城市管理的效率。如中期科技「智慧公厕-智慧厕所」通过运用先进的科技手段&#xff0c;智慧公厕在优化城市管理、提升城市服务、促进…

【c++_containers】string的模拟实现

前言 在学习数据结构时&#xff0c;如何证明自己彻底掌握了一个容器的各种特性&#xff1f;最直接的办法就是自己写一个。下面我们将围绕下图展开对与string的深度了解&#xff1a; 一.string的成员变量 string是表示字符序列的对象&#xff0c;同时增加了专门用于操作单字节字…

MySQL-三大日志

前言 ​ redo log&#xff1a;为了持久化数据&#xff0c;当内存中的数据还没写入到磁盘而宕机时&#xff0c;会读取该日志持久化数据到磁盘 ​ undo log&#xff1a;为了保证原子性&#xff0c;事务的操作都会记录一条相反的sql到该日志&#xff0c;出现错误就会根据该文件恢…

word-doc和docx区别

office从业者路过。 文件结构上doc文件数据是以二进制形式存放的。 docx是以xml文件形式存放的。 doc兼容较差&#xff0c;docx效果更好。

【文末赠书】SRE求职必会 —— 可观测性平台可观测性工程(Observability Engineering)

文章目录 〇、导读一、实现可观测性平台的技术要点是什么&#xff1f;二、兼容全域信号量三、所谓全域信号量有哪些&#xff1f;四、统一采集和上传工具五、统一的存储后台六、自由探索和综合使用数据七、总结★推荐阅读《可观测性工程》直播预告直播主题直播时间预约直播 视频…

Python实现机器学习(下)— 数据预处理、模型训练和模型评估

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。本门课程将介绍人工智能相关概念&#xff0c;重点讲解机器学习原理机器基本算法&#xff08;监督学习及非监督学习&#xff09;。使用python&#xff0c;结合sklearn、Pycharm进行编程&#xff0c;介绍iris&#xff08;鸢尾…

Kubernetes基础-外部访问方式NodePort/LoadBalancer/Ingress

1 概述 NodePort、LoadBalancer 和 Ingress 都是将集群外部流量导入到集群内的方式&#xff0c;只是实现方式不同。以下是三种方式的工作原理 注意&#xff1a;这里说的每一点都基于Google Kubernetes Engine。如果用 minikube 或其它工具&#xff0c;以预置型模式&#xff08…

RocketMQ 源码分析——Producer

文章目录 消息发送代码实现消息发送者启动流程检查配置获得MQ客户端实例启动实例定时任务 Producer 消息发送流程选择队列默认选择队列策略故障延迟机制策略*两种策略的选择 技术亮点:ThreadLocal 消息发送代码实现 下面是一个生产者发送消息的demo&#xff08;同步发送&#…

信息搜集-指纹识别

自存收藏链接 【安全】【信息搜集】网站篇-指纹识别 如何查看web服务器的名称和版本号 网站分析技巧&#xff0c;手工检测Web网站指纹 黑客工具之whatweb详细使用教程 渗透测试信息收集-CMS指纹识别

SpringMVC返回不带引号的字符串方案汇总

SpringMVC返回不带引号的字符串方案汇总 问题 项目使用springboot开发的&#xff0c;大部分出参为json&#xff0c;使用的fastJson。 现在有的接口需要返回一个success字符串&#xff0c;发现返回结果为“success”&#xff0c;多带了双引号。这是因为fastJson对出参做了处理…

C++--哈希表的实现及unorder_set和unorder_map的封装

1.什么是哈希表 哈希表是一种数据结构&#xff0c;用来存放数据的&#xff0c;哈希表存放的数据是无序的&#xff0c;可以实现增删查&#xff0c;当时不能修改数据。可以不经过任何比较&#xff0c;一次直接从表中得到要搜索的元素。如果构造一种存储结构&#xff0c;通过某种函…

【DETR】End-to-End Object Detection with Transformers

End-to-End Object Detection with Transformers   整个模型的主要思想是把物体检测问题看作一个集合到集合的预测问题&#xff0c;将图片切分成一个个Patches。然后进行位置编码&#xff0c;利用Transformer Encoder和Decoder进行编码和解码&#xff0c;最后使用FFN进行分类…

golang 自动生成文件头

安装koroFileHeader控件 打开首选项&#xff0c;进入设置&#xff0c;配置文件头信息"fileheader.customMade": {"Author": "lmy","Date": "Do not edit", // 文件创建时间(不变)// 文件最后编辑者"LastEditors"…

5.14 Get Log Page Command

5.14 Get Log Page command “Get Log Page”命令返回一个数据缓冲区&#xff0c;其中包含请求的日志页。 Get Log Page命令使用Data Pointer&#xff0c;DWord10, DWord11, DWord12, DWord13, DWord14这几个字段。其他命令字段是保留的。 图191和图192中定义了强制和可选的日…

Postgresql并行框架随手记

使用方法 EnterParallelMode()CreateParallelContext(“library_name”, “function_name”, nworkers) 指定并发数&#xff0c;bgworker拉起几个进程干活。 shm_toc_estimate_chunk/shm_toc_estimate_keys 评估大小写入pcxt->estimator 先评估全部要进入共享内存的大小。 …

Linux 在线解压jar包

在 CentOS 中解压 jar 包可以使用 unzip 命令。unzip 命令用于解压缩各种压缩文件&#xff0c;包括 jar 包。 以下是解压 jar 包的步骤&#xff1a; 将 jar 包下载到本地。使用 unzip 命令解压 jar 包。 # 解压 jar 包 unzip jar_file.jar例如&#xff0c;要解压 jar_file.j…

Windows10下的GTSAM因子图安装与使用

Windows10下的GTSAM因子图安装与使用 一、windows系统预安装1. windows 10安装gcc2.windows 10 安装 boost3.CMake 安装与查看4.CMake 配置boost 二、GTSAM安装与使用三、CMAKE 创建立 使用GTSAM的Visual Studio项目参考文献 一、windows系统预安装 1. windows 10安装gcc htt…

大数据分析(Python)学习笔记1(python基础快速过)

第 1 部分 基础篇 第1章 Python语言基础 1.2 语法基础&#xff08;快速过一遍&#xff09; 1.代码注释方式 注释代码有以下两种方法&#xff1a; &#xff08;1&#xff09;在一行中&#xff0c;“#”后的语句不被执行&#xff0c;表示被注释。 &#xff08;2&#xff09…

【考研数学】线性代数第五章 —— 特征值和特征向量(2,特征值与特征向量的性质)

文章目录 引言二、特征值与特征向量的性质2.1 一般性质2.2 实对称矩阵特征值与特征向量的性质 写在最后 引言 承接前文&#xff0c;了解了一些基本概念后&#xff0c;我们来继续学习有关特征值和特征向量的内容。 二、特征值与特征向量的性质 2.1 一般性质 定理 1 —— 设 …

Linux下运行Jmeter压测

一、在Linux服务器先安装SDK 1、先从官网下载jdk1.8.0_131.tar.gz&#xff0c;再从本地上传到Linux服务器 2、解压&#xff1a;tar -xzf jdk1.8.0_131.tar.gz&#xff0c;生成文件夹 jdk1.8.0_131 3、在/usr/目录下创建java文件夹&#xff0c;再将 jdk1.8.0_131目录移动到/u…
最新文章