【C++】Vector核心实现:类设计到迭代器陷阱

vector 模拟实现代码的核心

下面从类设计、核心接口、内存安全、常见陷阱、测试场景5 个维度,提炼需重点掌握的知识点,覆盖面试高频考点与实践易错点:

一、类结构与成员变量(基础框架)

vector 的核心是通过三个迭代器(指针) 管理动态内存,这是理解所有接口的前提:

  • _start:指向动态数组的起始位置(首元素)

  • _finish:指向动态数组中已使用元素的下一个位置(size = _finish - _start

  • _end_of_storage:指向动态数组的末尾位置(capacity = _end_of_storage - _start

  • 成员变量用C++11 类内初始值(= nullptr)初始化,因此默认构造可空实现(vector() {}),简化代码。

二、构造函数设计(重载与模板陷阱)

构造函数是 vector 的 “入口”,需重点关注重载匹配问题与通用性:

  1. 避免模板构造函数的匹配陷阱

代码中提供了两个n, val构造的重载:

vector(size_t n, const T& val = T()); // 1. size_t版本
vector(int n, const T& val = T());    // 2. int版本
为什么需要int版本?(没懂)

若只写size_t n版本,当用户传入int类型的n(如vector<int> v(10, 5),10 是int),编译器会优先匹配模板迭代器构造(vector(InputIterator first, InputIterator last))—— 因为int可被推导为InputIterator类型,导致将105当作 “迭代器范围”,而int不是迭代器,直接编译报错。

注:

  • 在 C++ 中,像 105 这样的整数字面量,默认类型是 int(有符号整型),而不是 size_t(无符号整型,通常是 unsigned intunsigned long long,取决于平台)。

  • 只有显式写 10u(加 u 表示无符号)或 size_t(10),才会被视为 size_t 类型。

  • 若只有 size_t 版本,编译器会优先选择模板迭代器构造函数(精确匹配),但 int 不是迭代器,导致报错;

  • 加个 int 版本后,它会作为 “非模板的精确匹配” 优先被选中,确保调用的是 “n 个 val 初始化” 的正确逻辑。

结论:必须重载int n版本,避免模板构造函数 “抢错” 匹配。

  1. 模板迭代器构造(通用性关键)

template <class InputIterator>
vector(InputIterator first, InputIterator last);
  • 作用:支持从任意 “迭代器范围”([first, last))初始化,如string的迭代器、原生数组指针、其他容器的迭代器。

  • 示例:

std::string s1("hello");
vector<int> v3(s1.begin(), s1.end()); // char转int,存储ASCII值
int a[] = {100,10,2};
vector<int> v4(a, a+3); // 原生数组指针作迭代器
  • 注意:迭代器类型需支持*解引用和++自增(符合 InputIterator 概念)。

  1. 拷贝构造(深拷贝的正确实现)

代码中拷贝构造的核心是避免浅拷贝:

vector(const vector<T>& v) {_start = new T[v.capacity()]; // 开和原对象capacity相同的空间// 循环赋值(深拷贝),而非memcpy(浅拷贝)for (size_t i = 0; i < v.size(); ++i) {_start[i] = v._start[i]; }_finish = _start + v.size();_end_of_storage = _start + v.capacity();
}
关键对比:为什么不用memcpy
  • memcpy是字节级拷贝(浅拷贝),仅适用于 POD 类型(如intdouble);

  • T是复杂类型(如string、自定义类),memcpy会导致两个对象的成员指针指向同一块内存,析构时重复释放(崩溃);

  • 循环赋值会调用T的赋值运算符(如string::operator=),完成深拷贝(为新string开辟独立内存)。

结论:动态数组存储非 POD 类型时,必须用循环赋值实现深拷贝。

三、核心接口实现(易错点与设计思路)

vector 的核心接口(reserve/resize/insert/erase)是面试高频考点,需关注功能差异、迭代器失效、扩容策略。

  1. reserve vs resize(最易混淆的接口)

两者均可能扩容,但核心作用完全不同:

接口作用对 size 的影响对 capacity 的影响元素初始化
reserve(n)仅保证 capacity≥n不改变若 n > 当前 capacity 则扩容,否则不做不初始化新元素
resize(n, val)保证 size=n改变(n<size 截断,n>size 补元素)若 n > 当前 capacity 则扩容,否则不做n>size 时,新增元素用val初始化(默认T())

示例:

v1.resize(10); // size从5→10,新增5个元素(int()=0),capacity若不足则扩容
v1.resize(3);  // size从10→3,仅移动_finish(_start+3),不释放内存
  1. insert(迭代器失效的典型场景)

insert(pos, val)的核心是解决扩容导致的 pos 失效:

iterator insert(iterator pos, const T& val) {if (_finish == _end_of_storage) {size_t len = pos - _start; // 记录pos到起始的距离(关键)reserve(/*扩容*/);pos = _start + len; // 扩容后更新pos(旧pos指向已释放内存)}// 元素后移(从后往前)iterator end = _finish - 1;while (end >= pos) {*(end + 1) = *end;--end;}*pos = val;++_finish;return pos; // 返回更新后的pos,方便用户后续使用
}
关键注意点:
  • 扩容后必须重新计算 pos:因为扩容会分配新内存,旧pos指向的旧内存已被释放(野指针);

  • 返回新pos:避免用户使用失效的迭代器(如 test_vector3 中pos = v1.insert(pos, 30)后,可安全修改*pos)。

  1. erase(循环删除的正确写法)

erase(pos)的核心是迭代器失效范围(仅pos之后的迭代器失效,pos本身被返回的新迭代器替代):

iterator erase(iterator pos) {// 元素前移(从pos+1开始)iterator start = pos + 1;while (start != _finish) {*(start - 1) = *start;++start;}--_finish;return pos; // 返回指向“原pos下一个元素”的迭代器
}
典型场景:循环删除满足条件的元素

错误写法(会导致迭代器失效,漏删或越界):'

for (auto it = v1.begin(); it != v1.end(); ++it) {if (*it % 2 == 0) {v1.erase(it); // erase后it失效,++it操作非法}
}

正确写法(用 erase 的返回值更新迭代器):

auto it = v1.begin();
while (it != v1.end()) {if (*it % 2 == 0) {it = v1.erase(it); // 用返回值更新it,指向删除后的下一个元素} else {++it; // 不删除则正常后移}
}
  1. push_back(扩容策略)

void push_back(const T& x) {if (_finish == _end_of_storage) {// 扩容策略:初始capacity=0→4,否则扩为2倍reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = x;++_finish;
}
扩容策略的意义:
  • 避免每次push_back都扩容(减少内存分配次数,提高效率);

  • 2 倍扩容是平衡 “内存利用率” 和 “分配次数” 的经典方案(1.5 倍扩容更优,但 2 倍实现简单)。

四、const 正确性(规范设计)

代码严格遵循 “const 对象只能调用 const 接口”,这是 C++ 的规范设计,需关注:

  1. const 迭代器:提供const_iterator begin() constconst_iterator end() const,支持 const 对象遍历(如func(const vector<int>& v)中使用v.begin());

  2. const 版本 operator []:

const T& operator[](size_t pos) const {assert(pos < size());return _start[pos];
}
  1. 确保 const 对象只能读取元素,不能修改(如funcv[i]无法赋值)。

五、测试用例中的典型场景(实践巩固)

代码中的测试用例覆盖了 vector 的核心使用场景,复习时需结合案例理解:

  1. test_vector5(循环删除):掌握erase后迭代器更新的正确写法;

  2. test_vector7(嵌套 vector):vector<vector<int>>实现杨辉三角,理解二维动态数组的管理(外层 vector 存储内层 vector,内层 vector 各自管理自己的内存);

  3. test_vector7(复杂类型拷贝):vector<string> v4(v3)验证深拷贝正确性(若用memcpyv3v4string会共享内存,析构崩溃);

  4. test_vector6(sort 排序):sort(v1.begin(), v1.end())说明 vector 的迭代器是随机访问迭代器(支持sort的要求),而 list 的迭代器不支持。

六、总结

  1. 三个迭代器的作用与size()/capacity()的计算方式;

  2. 拷贝构造为什么不能用memcpy(深拷贝 vs 浅拷贝);

  3. reserveresize的功能差异(是否改变 size、是否初始化元素);

  4. inserterase的迭代器失效问题(如何避免、返回值的作用);

  5. 循环删除元素的正确写法(结合erase的返回值);

  6. 模板构造函数的匹配陷阱(为什么需要int n的重载)。

#pragma once
#include<assert.h>namespace bit
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;//vector()//        :_start(nullptr)//        , _finish(nullptr)//        , _end_of_storage(nullptr)//{}//vector(size_t n, const T& val = T())//        : _start(nullptr)//        , _finish(nullptr)//        , _end_of_storage(nullptr)//{//        reserve(n);//        for (size_t i = 0; i < n; ++i)//        {//                push_back(val);//        }//}//// [first, last)//template <class InputIterator>//vector(InputIterator first, InputIterator last)//        : _start(nullptr)//        , _finish(nullptr)//        , _end_of_storage(nullptr)//{//        while (first != last)//        {//                push_back(*first);//                ++first;//        }//}vector(){}//vector<int> v(10, 5);vector(size_t n, const T& val = T()){reserve(n);for (size_t i = 0; i < n; ++i){push_back(val);}}vector(int n, const T& val = T()){reserve(n);for (int i = 0; i < n; ++i){push_back(val);}}/*vector(const vector<T>& v){reserve(v.capacity());for (auto e : v){push_back(e);}}*/vector(const vector<T>& v){_start = new T[v.capacity()];//memcpy(_start, v._start, sizeof(T)*v.size());for (size_t i = 0; i < v.size(); ++i){_start[i] = v._start[i];}_finish = _start + v.size();_end_of_storage = _start + v.capacity();}// [first, last)template <class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);++first;}}~vector(){delete[] _start;_start = _finish = _end_of_storage = nullptr;}iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}void resize(size_t n, T val = T()){if (n < size()){// 删除数据_finish = _start + n;}else{if (n > capacity())reserve(n);while (_finish != _start+n){*_finish = val;++_finish;}}}void reserve(size_t n){if (n > capacity()){size_t sz = size();T* tmp = new T[n];if (_start){//memcpy(tmp, _start, sizeof(T)*size());//深拷贝for (size_t i = 0; i < sz; ++i){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + sz;_end_of_storage = _start + n;}}void push_back(const T& x){if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : capacity()*2);}*_finish = x;++_finish;}void pop_back(){assert(!empty());--_finish;}iterator insert(iterator pos, const T& val){assert(pos >= _start);assert(pos <= _finish);if (_finish == _end_of_storage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);// 扩容后更新pos,解决pos失效的问题pos = _start + len;}iterator end = _finish-1;while (end >= pos){*(end + 1) = *end;--end;}*pos = val;++_finish;return pos;}iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);iterator start = pos + 1;while (start != _finish){*(start - 1) = *start;++start;}--_finish;return pos;}size_t capacity() const{return _end_of_storage - _start;}size_t size() const{return _finish - _start;}bool empty(){return _start == _finish;}T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _end_of_storage = nullptr;};void func(const vector<int>& v){for (size_t i = 0; i < v.size(); ++i){cout << v[i] << " ";}cout << endl;vector<int>::const_iterator it = v.begin();while (it != v.end()){cout << *it << " ";++it;}cout << endl << endl;}void test_vector1(){vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);func(v1);for (size_t i = 0; i < v1.size(); ++i){cout << v1[i] << " ";}cout << endl;v1.pop_back();v1.pop_back();vector<int>::iterator it = v1.begin();while (it != v1.end()){cout << *it << " ";++it;}cout << endl;v1.pop_back();v1.pop_back();v1.pop_back();//v1.pop_back();for (auto e : v1){cout << e << " ";}cout << endl;//func(v1);}//template<class T>//void f()//{//        T x = T();//        cout << x << endl;//}//void test_vector2()//{//        // 内置类型有没有构造函数///*        int i = int();//        int j = int(1);*///        f<int>();//        f<int*>();//        f<double>();//}void test_vector2(){vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);cout << v1.size() << endl;cout << v1.capacity() << endl;v1.resize(10);cout << v1.size() << endl;cout << v1.capacity() << endl;func(v1);v1.resize(3);func(v1);}void test_vector3(){std::vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);//v1.push_back(5);for (auto e : v1){cout << e << " ";}cout << endl;/*v1.insert(v1.begin(), 0);for (auto e : v1){cout << e << " ";}cout << endl;*/auto pos = find(v1.begin(), v1.end(), 3);if (pos != v1.end()){//v1.insert(pos, 30);pos = v1.insert(pos, 30);}for (auto e : v1){cout << e << " ";}cout << endl;// insert以后我们认为pos失效了,不能再使用(*pos)++;for (auto e : v1){cout << e << " ";}cout << endl;}void test_vector4(){std::vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);for (auto e : v1){cout << e << " ";}cout << endl;//auto pos = find(v1.begin(), v1.end(), 2);auto pos = find(v1.begin(), v1.end(), 4);if (pos != v1.end()){v1.erase(pos);}(*pos)++;for (auto e : v1){cout << e << " ";}cout << endl;}void test_vector5(){bit::vector<int> v1;v1.push_back(10);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);v1.push_back(50);bit::vector<int>::iterator it = v1.begin();while (it != v1.end()){if (*it % 2 == 0){it = v1.erase(it);}else{++it;}}for (auto e : v1){cout << e << " ";}cout << endl;for (auto e : v1){cout << e << " ";}cout << endl;}void test_vector6(){//vector<int> v(10u, 5);vector<int> v1(10, 5);for (auto e : v1){cout << e << " ";}cout << endl;vector<int> v2(v1.begin()+1, v1.end()-1);for (auto e : v2){cout << e << " ";}cout << endl;std::string s1("hello");vector<int> v3(s1.begin(), s1.end());for (auto e : v3){cout << e << " ";}cout << endl;int a[] = { 100, 10, 2, 20, 30 };vector<int> v4(a, a+3);for (auto e : v4){cout << e << " ";}cout << endl;v1.insert(v1.begin(), 10);for (auto e : v1){cout << e << " ";}cout << endl;sort(v1.begin(), v1.end());for (auto e : v1){cout << e << " ";}cout << endl;for (auto e : a){cout << e << " ";}cout << endl;//sort(a, a+sizeof(a)/sizeof(int));/*        greater<int> g;sort(a, a + sizeof(a) / sizeof(int), g);*/sort(a, a + sizeof(a) / sizeof(int), greater<int>());for (auto e : a){cout << e << " ";}cout << endl;}class Solution {public:vector<vector<int>> generate(int numRows) {vector<vector<int>> vv;vv.resize(numRows, vector<int>());for (size_t i = 0; i < vv.size(); ++i){vv[i].resize(i + 1, 0);vv[i][0] = vv[i][vv[i].size() - 1] = 1;}for (size_t i = 0; i < vv.size(); ++i){for (size_t j = 0; j < vv[i].size(); ++j){if (vv[i][j] == 0){vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];}}}return vv;}};void test_vector7(){vector<int> v1(10, 5);for (auto e : v1){cout << e << " ";}cout << endl;vector<int> v2(v1);for (auto e : v2){cout << e << " ";}cout << endl;vector<std::string> v3(3, "111111111111111111111");for (auto e : v3){cout << e << " ";}cout << endl;vector<std::string> v4(v3);for (auto e : v4){cout << e << " ";}cout << endl;v4.push_back("2222222222222222222");v4.push_back("2222222222222222222");v4.push_back("2222222222222222222");for (auto e : v4){cout << e << " ";}cout << endl;vector<vector<int>> ret = Solution().generate(5);for (size_t i = 0; i < ret.size(); ++i){for (size_t j = 0; j < ret[i].size(); ++j){cout << ret[i][j] << " ";}cout << endl;}cout << endl;}
}

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

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

相关文章

并发编程指南 内存模型

文章目录5.1 内存模型5.1.1 对象和内存位置5.1.2 对象、内存位置和并发5.1.3 修改顺序5.1 内存模型 内存模型&#xff1a;一方面是内存布局&#xff0c;另一方面是并发。并发的基本结构很重要&#xff0c;特别是低层原子操作。因为C所有的对象都和内存位置有关&#xff0c;所以…

血缘元数据采集开放标准:OpenLineage Integrations Compatibility Tests Structure

OpenLineage 是一个用于元数据和血缘采集的开放标准&#xff0c;专为在作业运行时动态采集数据而设计。它通过统一的命名策略定义了由作业&#xff08;Job&#xff09;、运行实例&#xff08;Run&#xff09;和数据集&#xff08;Dataset&#xff09; 组成的通用模型&#xff0…

执行一条select语句期间发生了什么?

首先是连接器的工作&#xff0c;嗯&#xff0c;与客户端进行TCP三次握手建立连接&#xff0c;校验客户端的用户名和密码&#xff0c;如果用户名和密码都对了&#xff0c;那么就会检查该用户的权限&#xff0c;之后执行的所有SQL语句都是基于该权限接着客户端就可以向数据库发送…

element el-select 默认选中数组的第一个对象

背景&#xff1a;在使用element组件的时候&#xff0c;我们期望默认选中第一个数值。这里我们默认下拉列表绑定的lable是中文文字&#xff0c;value绑定的是数值。效果展示&#xff1a;核心代码&#xff1a;<template><el-select v-model"selectValue" plac…

【论文阅读】LightThinker: Thinking Step-by-Step Compression (EMNLP 2025)

论文题目&#xff1a;LightThinker: Thinking Step-by-Step Compression 论文来源&#xff1a;EMNLP 2025&#xff0c;CCF B 论文作者&#xff1a; 论文链接&#xff1a;https://arxiv.org/abs/2502.15589 论文源码&#xff1a;https://github.com/zjunlp/LightThinker 一、…

ABAQUS多尺度纤维增强混凝土二维建模

本案例是通过ABAQUS对论文Study on the tensile and compressive mechanical properties of multi-scale fiber-reinforced concrete: Laboratory test and mesoscopic numerical simulation&#xff08;https://doi.org/10.1016/j.jobe.2024.108852&#xff09;中纤维增强混凝…

C++ ---- 模板的半特化与函数模板的偏特化

在 C 中&#xff0c;模板提供了一种强大的泛型编程方式&#xff0c;使得我们能够编写类型无关的代码。然而&#xff0c;在实际使用中&#xff0c;有时我们需要根据具体的类型或类型组合对模板进行定制&#xff0c;这时就需要用到模板的特化。本文将介绍半模板特化和函数模板的偏…

为何 React JSX 循环需要使用 key

key 是 React 用于识别列表中哪些子元素被改变、添加或删除的唯一标识符 它帮助 React 更高效、更准确地更新和重新渲染列表 1、核心原因&#xff1a;Diff算法与性能优化 React 的核心思想之一是通过虚拟 DOM (Virtual DOM) 来减少对真实 DOM 的直接操作&#xff0c;从而提升性…

Jetson AGX Orin平台R36.3.0版本1080P25fps MIPI相机图像采集行缺失调试记录

1.前言 主板:AGX Orin 官方开发套件 开发版本: R36.3.0版本 相机参数如下: 相机硬件接口: 2. 梳理大致开发流程 核对线序/定制相机转接板 编写camera driver驱动 编写camera dts配置文件 调camera参数/测试出图 前期基本流程就不多介绍了直接讲正题 3. 问题描述 …

力扣hot100:螺旋矩阵(边界压缩,方向模拟)(54)

在解决螺旋矩阵问题时&#xff0c;我们需要按照顺时针螺旋顺序遍历矩阵&#xff0c;并返回所有元素。本文将分享两种高效的解决方案&#xff1a;边界收缩法和方向模拟法。题目描述边界收缩法边界收缩法通过定义四个边界&#xff08;上、下、左、右&#xff09;来模拟螺旋遍历的…

[嵌入式embed][Qt]Qt5.12+Opencv4.x+Cmake4.x_用Qt编译linux-Opencv库 测试

[嵌入式embed][Qt]Qt5.12Opencv4.xCmake4.x_用Qt编译linux-Opencv库 & 测试前文:准备环境安装qt-opencv必备库git-clone opencv库编译opencv库特殊:opencv编译的include,编译出来后多嵌套了一层文件夹,手工处理下改为include/opencv2测试demo新建项目QOpencv3.promain.cpp百…

百度智能云「智能集锦」自动生成短剧解说,三步实现专业级素材生产

备受剪辑压力困扰的各位自媒体老板、MCN 同学们、投放平台大佬们&#xff0c;解放双手和大脑的好机会它来了&#xff01; 在这个数字化飞速发展的时代&#xff0c;智能技术正以前所未有的速度改变着我们的生活与工作方式。百度智能云&#xff0c;作为智能科技的引领者&#xf…

FPGA笔试面试常考问题及答案汇总

经历了无数的笔试面试之后&#xff0c;不知道大家有没有发现FPGA的笔试面试还是有很多共通之处和规律可循的。所以一定要掌握笔试面试常考的问题。FPGA设计方向&#xff08;部分题目&#xff09;1. 什么是同步逻辑和异步逻辑&#xff1f;同步逻辑 是指在同一个时钟信号的控制下…

从0开始的github学生认证并使用copilot教程(超详细!)

目录 一.注册github账号 1.1、仅仅是注册 1.2、完善你的profile 二、Github 学生认证 邮箱 学校名称 How do you plan to use Github? Upload Proof 学校具体信息 一.注册github账号 1.1、仅仅是注册 1.用如QQ邮箱的第三方邮箱注册github 再添加.edu结尾的教育邮箱&…

自动驾驶叉车与 WMS 集成技术方案:数据交互、协议适配与系统对接实现

自动驾驶叉车与仓库管理系统&#xff08;WMS&#xff09;是现代物流自动化的核心。当这两项技术协同工作时&#xff0c;仓库将实现前所未有的效率、准确性和可扩展性。以下是利用其集成实现最佳效果的方法。 为何集成至关重要 仓库管理在当今运营中扮演着至关重要的角色&…

“企业版维基百科”Confluence

“企业版维基百科”Confluence Confluence 是一款由澳大利亚公司 Atlassian 开发的企业级团队协作与知识管理软件。您可以把它理解为一个功能非常强大的 “企业版维基百科” 或 “团队知识库”。 它的核心目标是帮助团队在一个统一的平台上创建、共享、组织和讨论项目文档、会议…

QT去除显示的红色和黄色下划线的办法

在使用 Qt Creator 开发项目时,有时候会遇到这样的情况: 代码明明没有错误,但编辑器里却出现了红色或黄色的下划线提示,甚至让人误以为代码有问题。其实,这通常是 Qt Creator 的代码模型没有及时更新 导致的,而不是项目本身的错误。 为什么会出现红色和黄色下划线? 红…

域内的权限提升

CVE-2020-1472域内有一个服务&#xff1a;MS-NRPC&#xff08;建立与域控安全通道&#xff09;&#xff0c;可利用此漏洞获取域管访问权限。检测这个漏洞能不能打&#xff0c;能打之后&#xff0c;将域控的机器hash置空&#xff0c;密码为空&#xff0c;那么你就可以通过空的ha…

一键掌握服务器健康状态与安全风险

一键掌握服务器健康状态与安全风险 在服务器运维工作中,定期对系统进行全面检查是保障服务稳定运行的关键环节。手动检查不仅耗时费力,还容易遗漏关键指标。今天我将为大家介绍一款功能全面的系统综合巡检工具,只需一键运行,即可完成系统状态、性能、安全等多维度检查,并…

线性代数第一讲—向量组

文章目录考纲术语向量组的线性表示与线性相关判别线性相关性的七大定理极大线性无关组、等价向量组、向量组的秩等价矩阵和等价向量组向量空间基本概念基变换、坐标变换 考纲术语 n维向量n维行向量n维列向量分量向量相等向量的加法向量的数乘向量的内积正交向量的模单位向量标准…