STL之vector篇(下)(手撕底层代码,从零实现vector的常用指令,深度剖析并优化其核心代码)

文章目录

    • 1.基本结构与初始化
      • 1.1 空构造函数的实现与测试
      • 1.2 带大小和默认值的构造函数
      • 1.3 使用迭代器范围初始化的构造函数(建议先看完后面的reserve和push_back)
      • 1.4 拷贝构造函数
      • 1.5 赋值操作符的实现(深拷贝)
      • 1.6 析构函数
      • 1.7 `begin` 与 `end` 迭代器
    • 2. 容量管理
      • 2.1 `reserve`函数:实现动态扩容
      • 2.2 `resize`函数:改变大小
    • 3. 元素插入与删除
      • 3.1 `push_back`函数:尾插元素
      • 3.2 `pop_back`函数:尾删元素
      • 3.3 `insert`函数:在指定位置插入元素
      • 3.4 `erase`函数:删除指定位置的元素
    • 4.全部代码实现
      • 4.1 头文件
      • 4.2 源文件


前言:

在编程的浩瀚星空中,数据结构如同指引方向的星辰,它们不仅是构建算法与程序大厦的基石,更是解决复杂问题、优化性能的关键所在。其中,vector作为C++标准模板库(STL)中的一颗璀璨明珠,凭借其灵活、高效的特点,在软件开发中占据了举足轻重的地位。

vector,即向量容器,是一种能够存储任意类型对象的动态数组。它封装了动态数组的所有特性,包括自动的内存管理、灵活的元素访问方式以及高效的随机访问性能,同时还提供了丰富的成员函数来支持元素的插入、删除、遍历等操作。与静态数组相比,vector的最大优势在于其大小可以动态变化,无需程序员手动管理内存,极大地简化了编程复杂度,提高了代码的安全性和可维护性。
本文旨在深入探讨vector的内部机制、使用技巧及最佳实践,帮助读者全面了解并掌握这一强大的数据结构。我们将从vector的基本概念和特性出发,逐步深入到其内存管理策略、迭代器使用、元素操作、性能优化等多个方面。同时,结合实际应用场景,展示vector在解决各种编程问题时的独特魅力和高效性。

无论你是C++编程的初学者,还是希望提升编程技能的老手,本文都将为你提供宝贵的参考和实用的指导。让我们一起踏上这场关于vector的探索之旅,解锁C++编程的新世界,感受数据结构的魅力与力量。

1.基本结构与初始化

1.1 空构造函数的实现与测试

  • 实现一个空的vector,不分配任何内存。
  • 测试是否创建了容量为0的vector

实现代码:

namespace xny {template<class T>class vector {public:typedef T* iterator;vector() {}size_t size() const {return _finish - _start;}size_t capacity() const {return _endofstorage - _start;}bool empty() const {return _start == _finish;}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorage = nullptr;};
}

测试样例:

void testvector1() {xny::vector<int> v;assert(v.size() == 0);  // 验证大小assert(v.capacity() == 0);  // 验证容量assert(v.empty());  // 验证是否为空cout << "testvector1 passed" << endl;
}int main()
{testvector1();return 0;
}

输出:

testvector1 passed

1.2 带大小和默认值的构造函数

  • 初始化一个给定大小的vector,并使用默认值填充。
  • 测试构造后大小、容量是否符合要求。

实现代码:

namespace xny {template<class T>class vector {public:typedef T* iterator;vector() {}vector(size_t n, const T& val = T()){    // 缺省参数用T类型的匿名对象_start = new T[n];_finish = _start + n;_endofstorage = _finish;for (size_t i = 0; i < n; i++) {_start[i] = val;  // 填充默认值}}T& operator[](size_t pos) { return _start[pos]; }size_t size() const {return _finish - _start;}size_t capacity() const {return _endofstorage - _start;}bool empty() const {return _start == _finish;}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorage = nullptr;};
}

测试样例:

void testvector2(){xny::vector<int> v(4, 1);assert(v.size() == 4);  // 验证大小assert(v.capacity() == 4);  // 验证容量assert(!v.empty());  // 验证是否为空for (size_t i = 0; i < 4; ++i) {assert(v[i] == 1);  // 验证默认值}cout << "testvector2 passed" << endl;
}int main()
{testvector3();return 0;
}

输出:

testvector2 passed

1.3 使用迭代器范围初始化的构造函数(建议先看完后面的reserve和push_back)

【思考】:

  • 假设我用上述代码去实现以下样例会发生什么呢?
void testvector3() {int arr[] = { 1, 2, 3, 4, 5 };xny::vector<int> v(arr, arr + 5); // 使用迭代器范围初始化构造函数
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

【结果】:

  • 这时我们意识到可能需要重新定义一个构造函数,并且这个构造函数可以使用迭代器范围初始化。

添加代码如下:

namespace xny {template<class T>class vector {public:typedef T* iterator;iterator begin(){ return _start; }iterator end(){ return _finish; }vector() {}vector(size_t n, const T& val = T()){    // 缺省参数用T类型的匿名对象_start = new T[n];_finish = _start + n;_endofstorage = _finish;for (size_t i = 0; i < n; i++) {_start[i] = val;  // 填充默认值}}// 使用迭代器范围初始化的构造函数template<class InputIterator>vector(InputIterator first, InputIterator last){while (first != last)	// 迭代器区间左闭右开{push_back(*first);++first;}}void reserve(size_t n){if (n > capacity()){size_t sz = size();T* tmp = new T[n];if (_start){for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + sz;_endofstorage = _start + n;}}void push_back(const T& x){if (_finish == _endofstorage){size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);}*_finish = x;++_finish;}T& operator[](size_t pos) { return _start[pos]; }size_t size() const {return _finish - _start;}size_t capacity() const {return _endofstorage - _start;}bool empty() const {return _start == _finish;}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorage = nullptr;};
}

测试样例:

void testvector3() {int arr[] = { 1, 2, 3, 4, 5 };xny::vector<int> v1(arr, arr + 5); // 使用迭代器范围构造函数for (auto e : v1){cout << e << " ";}cout << endl;string str("hello world");xny::vector<char> v2(str.begin(), str.end());for (auto e : v2) {cout << e << " ";}// 这里我们用两种不同类型的迭代器进行测试
}

输出:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

【思考】:

  • 怎么回事呢?编译器又报错了,并且这次报的错比较模糊,于是我们可以直接搜索error C2100,看看编译示例:

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

【解释】:

  • 结合以上所有的编译报错我们终于发现错误的原因可能是非法寻址了,于是再次进行改进添加代码如下:
vector(int n, const T& val = T()) {    _start = new T[n];_finish = _start + n;_endofstorage = _finish;for (size_t i = 0; i < n; i++) {_start[i] = val; }
}

输出:

1 2 3 4 5
h e l l o   w o r l d

1.4 拷贝构造函数

  • 实现vector的拷贝构造函数。
  • 测试拷贝后的vector是否完全复制原来的内容和容量。

实现代码:

namespace xny {template<class T>class vector {public:typedef T* iterator;vector() {}vector(size_t n, const T& val = T()){    _start = new T[n];_finish = _start + n;_endofstorage = _finish;for (size_t i = 0; i < n; i++) {_start[i] = val;  }}vector(const vector<T>& v){size_t sz = v.size();   _start = new T[capacity()];for (size_t i = 0; i < sz; i++) {_start[i] = v._start[i];}_finish = _start + sz;_endofstorage = _start + v.capacity();}T& operator[](size_t pos) { return _start[pos]; }size_t size() const {return _finish - _start;}size_t capacity() const {return _endofstorage - _start;}bool empty() const {return _start == _finish;}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorage = nullptr;};
}

测试样例:

void testvector4() {xny::vector<int> v1(5, 10);xny::vector<int> v2(v1);assert(v2.size() == 5);  // 验证大小assert(v2.capacity() == 5);  // 验证容量for (size_t i = 0; i < 5; ++i) {assert(v2[i] == 10);  // 验证数据拷贝}std::cout << "testvector4 passed" << std::endl;
}int main()
{testvector4();return 0;
}

输出:

testvector4 passed

1.5 赋值操作符的实现(深拷贝)

  • 实现赋值操作符重载。
  • 测试两个vector赋值后,是否正确拷贝了内容和容量。

实现代码:

namespace xny {template<class T>class vector {public:typedef T* iterator;vector() {}vector(size_t n, const T& val = T()){    _start = new T[n];_finish = _start + n;_endofstorage = _finish;for (size_t i = 0; i < n; i++) {_start[i] = val;  }}// 自定义一个swap函数方便拷贝void swap(const vector<T> v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}// 赋值运算符重载vector<T>& operator=(const vector<T>& v){swap(v);return *this;}T& operator[](size_t pos) { return _start[pos]; }size_t size() const {return _finish - _start;}size_t capacity() const {return _endofstorage - _start;}bool empty() const {return _start == _finish;}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorage = nullptr;};
}

【说明】:

  • **传值参数:**通过传递 vector<T> 的值作为参数,创建一个临时对象 v。调用拷贝构造函数时自动执行拷贝,然后在赋值操作中与现有对象交换内容。传值是安全的,避免了手动内存管理问题。
  • **swap函数:**通过交换数据成员,避免手动内存释放,简化代码逻辑。交换后的临时对象 v 离开作用域时自动销毁,保证资源释放。

测试样例:

void testvector5() {xny::vector<int> v1(5, 10);xny::vector<int> v2 = v1;  // 赋值操作assert(v2.size() == 5);  // 验证大小assert(v2.capacity() == 5);  // 验证容量for (size_t i = 0; i < 5; ++i) {assert(v2[i] == 10);  // 验证数据拷贝}std::cout << "testvector5 passed" << std::endl;
}int main()
{testvector5();return 0;
}

输出:

testvector5 passed

1.6 析构函数

实现代码:

~vector()
{if (_start){delete[] _start;_start = _finish = _endofstorage = nullptr;}
}

【注意】

  • 为了避免内存泄露的问题,上述代码全部都需要添加析构函数。

1.7 beginend 迭代器

【解释】:

  • begin 函数返回指向 vector 起始位置的迭代器(即指向第一个元素)。
  • end 函数返回指向 vector 末尾的迭代器(即指向最后一个元素的下一个位置)。
  • 两者结合可以用于遍历 vector 中的元素。

添加代码:

iterator begin(){return _start;
}iterator end(){return _finish;
}

2. 容量管理

2.1 reserve函数:实现动态扩容

  • 实现reserve函数,测试在容量不足时是否能正确扩展。

添加代码:

void reserve(size_t n) {if (n > capacity()) {size_t sz = size();T* tmp = new T[n];if (_start) {for (size_ t i = 0; i < sz; i++) {tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + sz;_endofstorage = _start + n;}
}

【注意】:

  • T是自定义类型时,依次调用数组每个对象的析构函数,再释放整个空间

  • 原来for循坏中使用的是memcpy(tmp, _start, sizeof(T) * sz);但是vector是深拷贝,但是vector空间上存的对象是string数组,使用memcpy导致string对象的浅拷贝

  • 解决方案:T是string这样深拷贝的类,调用的是string赋值重载,实现的是string对象的深拷贝

测试用例:

void testvector6() {xny::vector<int> v(5, 10);v.reserve(10);  // 预留容量assert(v.capacity() == 10);  // 验证容量扩展for (size_t i = 0; i < 5; ++i) {assert(v[i] == 10);  // 验证数据保持不变}std::cout << "testvector6 passed" << std::endl;
}int main()
{testvector6();return 0;
}

输出:

testvector6 passed

2.2 resize函数:改变大小

  • 实现resize函数,测试增加或减少vector大小,如果增加,用特定字符暂时覆盖。

添加代码:

void resize(size_t n, const T& val = T()) {if (n < size()) {_finish = _start + n;}else {// 扩容nreserve(n);// 传值while (_finish != _start + n) {*_finish = val;++_finish;}}
}

测试用例:

void testvector7() {xny::vector<int> v(5, 10);v.resize(8, 20);  // 扩展大小并填充新值assert(v.size() == 8);  // 验证扩展后大小for (size_t i = 0; i < 5; ++i) {assert(v[i] == 10);  // 验证原值不变}for (size_t i = 5; i < 8; ++i) {assert(v[i] == 20);  // 验证新值}std::cout << "testvector7 passed" << std::endl;
}int main()
{testvector7();return 0;
}

输出:

testvector7 passed

3. 元素插入与删除

3.1 push_back函数:尾插元素

【实现步骤】:

    1. 检查容量是否足够,若不足则扩容(通常容量加倍)。
    1. 将新元素插入到当前末尾。
    1. 更新_finish指针,指向新的末尾。

添加代码:

void push_back(const T& val){if (_finish == _endofstorage) {size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;  // 采用2倍扩容reserve(newcapacity);}*_finish = val;++_finish;
}

优化版本:

void push_back(const T& x){insert(end(), x);
}

测试用例:

void testvector8() {xny::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);assert(v.size() == 3);  // 验证插入后的大小assert(v.capacity() >= 3);  // 验证容量是否自动扩展assert(v[0] == 1 && v[1] == 2 && v[2] == 3);  // 验证插入的元素是否正确std::cout << "testvector9 passed" << std::endl;
}int main()
{testvector8();return 0;
}

输出:

testvector8 passed

3.2 pop_back函数:尾删元素

【实现步骤】:

    1. _finish指针向前移动一位,即删除最后一个元素。
    1. 不释放空间。

添加代码:

void pop_back(const T& val) {assert(_finish != _start);  // 确保vector非空--_finish;  // 逻辑删除最后一个元素
}

优化版本:

void pop_back(){erase(--end());
}

测试用例:

void testvector9() {xny::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.pop_back();assert(v.size() == 2);  // 验证删除后的大小assert(v[0] == 1 && v[1] == 2);  // 验证剩余元素是否正确std::cout << "testvector9 passed" << std::endl;
}int main()
{testvector9();return 0;
}

输出:

testvector9 passed

3.3 insert函数:在指定位置插入元素

【实现步骤】:

    1. 检查容量是否足够,不足时扩容。
    1. 将插入位置及其后的元素整体向后移动。
    1. 将新元素插入指定位置。
    1. 更新_finish指针。

添加代码:

iterator insert(iterator pos, const T& val) {// 断言判断pos的有效性assert(pos >= _start && pos <= _finish);if (_finish == _endofstorage) {// 用临时变量存指定位置到起点的长度size_t len = pos - _start;size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;  // 采用2倍扩容reverse(newcapacity);// 解决pos迭代器失效问题pos = len + _start;}// 将插入位置及其后的元素整体向后移动。iterator end = _finish - 1;while (end >= pos) {*(end + 1) = *end;--end;}*pos = val;++_finish;return pos;
}

测试用例:

void testvector10() {xny::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(4);v.insert(v.begin() + 2, 3);  // 在第2个位置插入3assert(v.size() == 4);  // 验证插入后的大小assert(v[0] == 1 && v[1] == 2 && v[2] == 3 && v[3] == 4);  // 验证插入的元素是否正确std::cout << "testvector8 passed" << std::endl;
}int main()
{testvector10();return 0;
}

输出:

testvector10 passed

【问题】:

  • 扩容之后,pos迭代器可能会发生改变,所以不能用原来的pos

3.4 erase函数:删除指定位置的元素

【实现步骤】:

    1. erase位置之后的元素向前移动一位。
    1. 更新_finish指针。

添加代码:

iterator erase(iterator pos) {// 断言判断pos的有效性assert(pos >= _start && pos <= _finish);iterator it = pos + 1;while (it != _finish) {*(it - 1) = *it;++it;}--_finish;return pos;
}

测试用例:

void testvector11() {xny::vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.erase(v.begin() + 2);  // 删除第2个元素assert(v.size() == 3);  // 验证删除后的大小assert(v[0] == 1 && v[1] == 2 && v[2] == 4);  // 验证删除后的元素顺序std::cout << "testvector11 passed" << std::endl;
}int main()
{testvector11();return 0;
}

输出:

testvector11 passed

4.全部代码实现

【注意】

  • 以下代码会提供常量和普通变量的两种函数或者迭代器。

4.1 头文件

#pragma once#include<assert.h>
using namespace std;namespace xny
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}vector(size_t n, const T& val = T()):_start(nullptr), _finish(nullptr), _endofstorage(nullptr){resize(n, val);}// 解决vector<int> v(10, 1)vector(int n, const T& val = T()):_start(nullptr), _finish(nullptr), _endofstorage(nullptr){resize(n, val);}template<class InputIterator>vector(InputIterator first, InputIterator last){while (first != last)	// 迭代器区间左闭右开{push_back(*first);++first;}}vector(){}vector(const vector<T>& v){size_t sz = v.size();_start = new T[v.capacity()];// memcpy(_start, v._start, sizeof(T) * v.size());for (size_t i = 0; i < sz; i++){_start[i] = v._start[i];}_finish = _start + sz;_endofstorage = _start + v.capacity();}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}// v1 = v2vector<T>& operator=(vector<T> v){swap(v);return *this;}~vector(){if (_start){delete[] _start;_start = _finish = _endofstorage = nullptr;}}void reserve(size_t n){if (n > capacity()){size_t sz = size();T* tmp = new T[n];if (_start){for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + sz;_endofstorage = _start + n;}}void resize(size_t n, const T& val = T())	// T类型的匿名对象{if (n < size()){_finish = _start + n;}else{reserve(n);while (_finish != _start + n){*_finish = val;++_finish;}}}void push_back(const T& x){/*if (_finish == _endofstorage){size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);}*_finish = x;++_finish;*/insert(end(), x);}void pop_back(){erase(--end());}size_t capacity() const{return _endofstorage - _start;}size_t size() const{return _finish - _start;}T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}iterator insert(iterator pos, const T& x){assert(pos >= _start && pos <= _finish);if (_finish == _endofstorage){size_t len = pos - _start;size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;reserve(newcapacity);// 解决pos迭代器失效问题pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}*pos = x;++_finish;return pos;}iterator erase(iterator pos){assert(pos >= _start && pos < _finish);iterator it = pos + 1;while (it != _finish){*(it - 1) = *it;++it;}--_finish;return pos;}private:iterator _start = nullptr;iterator _finish = nullptr;iterator _endofstorage = nullptr;};void print(const vector<int>& v){for (auto e : v){cout << e << " ";}cout << 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);for (auto e : v1){cout << e << " ";}cout << endl;for (size_t i = 0; i < v1.size(); i++){v1[i]++;}for (auto e : v1){cout << e << " ";}cout << endl;print(v1);}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);v1.push_back(5);v1.push_back(5);v1.push_back(5);for (auto e : v1){cout << e << " ";}cout << endl;v1.insert(v1.begin(), 100);for (auto e : v1){cout << e << " ";}cout << endl;/*vector<int>::iterator p = v1.begin() + 3;v1.insert(p, 300);*/xny::vector<int>::iterator p = v1.begin() + 3;//v1.insert(p+3, 300);// insert以后迭代器可能会失效(扩容)// 记住,insert以后就不要使用这个形参迭代器了,因为他可能失效了v1.insert(p, 300);// 高危行为// *p += 10;for (auto e : v1){cout << e << " ";}cout << endl;}void test_vector3(){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.erase(v1.begin());auto it = v1.begin() + 4;v1.erase(it);// erase之后,迭代器失效了,不能访问// vs进行强制检查,访问会直接报错cout << *it << endl;++it;cout << *it << endl;for (auto e : v1){cout << e << " ";}cout << endl;}void test_vector4(){vector<int> v;v.resize(10, 0);for (auto e : v){cout << e << " ";}cout << endl;}void test_vector5(){vector<int> v1;v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.push_back(5);vector<int> v2(v1);for (auto e : v1){cout << e << " ";}cout << endl;vector<int> v3 = v1;for (auto e : v3){cout << e << " ";}cout << endl;};void test_vector6(){vector<string> v;v.push_back("11111");v.push_back("22222");v.push_back("33333");v.push_back("44444");v.push_back("55555");// 自定义类型最好加引用for (auto& e : v){cout << e << " ";}cout << endl;vector<string> v1(v);for (auto& e : v1){cout << e << " ";}cout << endl;}void test_vector7(){vector<int> v(10u, 1);vector<string> v1(10, "111");// vector<int> v2(10, 1);}
}

4.2 源文件

#include"vector.h"
#include <bits/stdc++.h>
using namespace std;int main()
{xny::test_vector7();// ...其他测试return 0;
}

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

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

相关文章

使用 sponge + dtm 轻松实现秒杀抢购服务(HTTP),彻底解决库存与订单数据不一致的难题

秒杀场景的挑战 秒杀是电商中常见的抢购商品场景&#xff0c;其技术特点是瞬间请求量巨大&#xff0c;对服务的性能和一致性要求极高。即使服务出现崩溃&#xff0c;也必须确保库存扣减和订单生成保持一致&#xff0c;避免出现超卖或超买的现象。通过使用 dtm&#xff08;分布…

【重要提示】由于找不到msvcr110.dll 无法继续执行的解决途径全面解析

在使用Windows操作系统时&#xff0c;您可能会遇到这样的问题&#xff1a;某些应用程序在启动时提示“由于找不到 msvcr110.dll&#xff0c;无法继续执行代码。重新安装程序可能会解决此问题。” 这种错误通常会导致应用程序无法正常运行&#xff0c;影响用户体验。本文将全面介…

MySQL 预处理语句:强大的数据库工具

《MySQL 预处理语句&#xff1a;强大的数据库工具》 在 MySQL 数据库的使用中&#xff0c;预处理语句是一个非常有用的功能。它可以提高数据库的性能、安全性和可维护性。那么&#xff0c;什么是预处理语句呢&#xff1f;它又有哪些优点呢&#xff1f;让我们一起来了解一下。 …

docker - 镜像操作(拉取、查看、删除)

文章目录 1、docker search --help&#xff08;用于显示 Docker 搜索命令的帮助信息&#xff09;2、docker pull&#xff08;拉取镜像&#xff09;3、docker images (查看镜像)3.1、docker images --help&#xff08;用于显示 Docker 镜像管理相关命令的帮助信息&#xff09;3.…

【数据结构】排序算法---桶排序

文章目录 1. 定义2. 算法步骤3. 演示3.1 动态演示13.2 动态演示23.3 图片演示13.4 图片演示2 4. 性质5. 算法分析6. 代码实现C语言PythonJavaCGo 结语 1. 定义 桶排序&#xff08;英文&#xff1a;Bucket sort&#xff09;是计数排序的升级版&#xff0c;适用于待排序数据值域…

Elasticsearch黑窗口启动乱码问题解决方案

问题描述 elasticsearch启动后有乱码现象 解决方案&#xff1a; 提示&#xff1a;这里填写该问题的具体解决方案&#xff1a; 到 \config 文件下找到 jvm.options 文件 打开后 在文件末尾空白处 添加 -Dfile.encodingGBK 保存后重启即可。

1. Linux系统(CentOS7.9)安装

toc 一、Linux概述介绍 1、Linux系统介绍 Linux, 一类操作系统的统称 部署在服务器上&#xff0c;部署项目、应用 服务器: 硬件设备, 柜式服务器&#xff0c;(华为、浪潮、联想) 提供服务的机器 2、Linux的优势 开源, open source , 开放源代码稳定性最大化发挥硬件资源 …

微服务注册中⼼1

1. 微服务的注册中⼼ 注册中⼼可以说是微服务架构中的”通讯录“ &#xff0c;它记录了服务和服务地址的映射关系。在分布式架构中&#xff0c; 服务会注册到这⾥&#xff0c;当服务需要调⽤其它服务时&#xff0c;就这⾥找到服务的地址&#xff0c;进⾏调⽤。 1.1 注册中⼼的…

【Redis入门到精通七】详解Redis持久化机制(AOF,RDB)

目录 Redis持久化机制 1.RDB持久化 &#xff08;1&#xff09;手动触发RDB持久化 &#xff08;2&#xff09;自动触发RDB持久化 &#xff08;3&#xff09;Redis文件相关处理 &#xff08;4&#xff09;RDB持久化的优缺点 2.AOF持久化 &#xff08;1&#xff09;AOF工作…

【隐私计算篇】利用多方安全计算MPC实现VGG16人脸识别隐私推理

1. 背景介绍 本文主要介绍一种利用多方安全计算MPC技术&#xff0c;实现VGG16的人脸识别模型&#xff0c;侧重于模型推理阶段&#xff0c;目前已经公开专利&#xff0c;因此以下内容的分享都是基于公开材料。该分享涉及到最小化多方安全计算(MPC)以及明密文混合计算的思想&…

签署《AI安全国际对话威尼斯共识》 智源持续推动人工智能安全发展

近日&#xff0c;由AI安全国际论坛&#xff08;Safe AI Forum&#xff09;和博古睿研究院&#xff08;Berggruen Institute) 共同举办的第三届国际AI安全对话&#xff08;International Dialogues on AI Safety&#xff09;在威尼斯举办。图灵奖得主Yoshua Bengio、姚期智教授&…

UBUNTU20.04安装CH384串口卡驱动

继续上文&#xff1a;统信UOS安装CH384串口卡驱动-CSDN博客 统信UOS系统成功安装CH384串口驱动后&#xff0c;继续在ubuntu20.04下安装驱动&#xff0c;发现一直报错&#xff0c;原因是内核驱动不一致。 解决办法&#xff1a; 1. 下载最新的驱动。CH35XCH384驱动源文件资源-C…

Java语言程序设计基础篇_编程练习题**18.30 (找出单词)

题目&#xff1a;**18.30 (找出单词) 编写一个程序&#xff0c;递归地找出某个目录下的所有文件中某个单词出现的次数。从命令行如下传递参数&#xff1a; java Exercise18_30 dirName word 习题思路 &#xff08;读取路径方法&#xff09;和18.28题差不多&#xff0c;把找…

Structure-Aware Transformer for Graph Representation Learning

Structure-Aware Transformer for Graph Representation Learning&#xff08;ICML22&#xff09; 摘要 Transformer 架构最近在图表示学习中受到越来越多的关注&#xff0c;因为它通过避免严格的结构归纳偏差而仅通过位置编码对图结构进行编码&#xff0c;自然地克服了图神经…

分享课程:VUE数据可视化教程

在当今这个数据驱动的世界中&#xff0c;数据可视化已经成为了一种至关重要的工具&#xff0c;它帮助我们理解复杂的数据集&#xff0c;发现模式、趋势和异常。数据可视化不仅仅是将数字转换成图表&#xff0c;它是一种将数据转化为洞察力的艺术。 1.什么是数据可视化&#xf…

C语言指针系列1——初识指针

祛魅&#xff1a;其实指针这块儿并不难&#xff0c;有人说难只是因为基础到进阶没有处理好&#xff0c;大家要好好跟着一步一步学习&#xff0c;今天我们先来认识一下指针 指针定义&#xff1a;指针就是内存地址&#xff0c;指针变量是用来存放内存地址的变量&#xff0c;在同一…

Java.动态代理

1.创建一个接口 package Mydynamicproxy1;public interface Star {public abstract String sing(String str);public abstract void dance(String str); }2.创建一个BigStar类&#xff0c;要实现Star这个接口 package Mydynamicproxy1;public class BigStar implements Star{…

webpack4 target:“electron-renderer“ 打包加速配置

背景 昨天写得一篇Electron-vue asar 局部打包优化处理方案——绕开每次npm run build 超级慢的打包问题-CSDN博客文章浏览阅读754次&#xff0c;点赞19次&#xff0c;收藏11次。因为组员对于 Electron 打包过程存在比较迷糊的状态&#xff0c;且自己也没主动探索 Electron-vu…

tcp、udp通信调试工具Socket Tool

tcp、udp通信调试工具Socket Tool ]

线程池的执行流程和配置参数总结

一、线程池的执行流程总结 提交线程任务&#xff1b;如果线程池中存在空闲线程&#xff0c;则分配一个空闲线程给任务&#xff0c;执行线程任务&#xff1b;线程池中不存在空闲线程&#xff0c;则线程池会判断当前线程数是否超过核心线程数&#xff08;corePoolSize&#xff09…