当前位置: 首页 > news >正文

【C++】vector的模拟实现

文章目录

  • 前言
  • 一. vector的底层
  • 二. 关于容量和大小的函数
    • 2.1 size和capacity
    • 2.2 reserve
    • 2.3 resize
    • 2.4 empty
  • 三. vector的默认成员函数
    • 3.1 构造函数
      • 3.1.1 无参构造函数
      • 3.1.2 构造初始化为n个val值
      • 3.1.3 用initializer_list构造初始化
      • 3.1.4 使用迭代器区间进行构造初始化
    • 3.2 拷贝构造函数
    • 3.3 赋值运算符重载
    • 3.4 析构函数
  • 四. iterator迭代器
  • 五. operator[]访问函数
  • 六. 修改容器内容的相关函数
    • 6.1 push_back
    • 6.2 pop_back()
    • 6.3 insert
    • 6.4 erase
    • 6.5 clear
  • 七. 源代码
  • END


前言

本篇博客讲的是vector的模拟实现,因为上一篇博客已经模拟了string的实现,所以在模拟vector的实现中会比较熟悉,模拟vector的实现是为了我们更好地理解和掌握vector,接下来我们会实现vector一些重要的接口。

一. vector的底层

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>
#include<vector>
using namespace std;
//用命名空间MyVector封装vector,避免与std库的vector冲突
namespace MyVector
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;private:iterator start; //指向数据块的开始iterator finish; //指向有效数据的末尾iterator end_of_storage; //指向存储容量的末尾};
}

vector的底层实现是由三个迭代器start,finish,end_of_storage来管理内存和数据的,而vector的迭代器就是一个原生态指针T*

  1. start:指向vector中第一个元素的地址。
  2. finish:指向vector中最后一个元素的下一个位置(即逻辑上的结束位置)。
  3. end_of_storage:指向vector分配的内存量的结束位置(即实际分配的内存大小)

注意:end_of_storage是一个指针,它指向分配的内存块的逻辑结束位置但并不是指向具体的内存单元,而是指向分配的内存块的末尾的下一个位置。它的作用是表示分配的内存的容量上限,而不是实际存储数据的地址。总的来说,end_of_storage是一个虚拟的边界标记

内存管理

  • start到finish:表示vector中实际存储的元素范围。
  • finish到end_of_storage:表示已分配但未使用的内存区域。

当 vector 的容量不足时(例如调用 push_back 或 resize),它会重新分配一块更大的内存区域,并将旧数据复制到新内存中,然后释放旧内存。这种机制确保了 vector 的动态扩展能力。

在这里插入图片描述

侯捷老师《STL源码剖析》的原图如下:

begin()就是start,end()就是finish

注意vector是一个类模板,所以在vector的实现中声明和定义不能分离

类模板的实例化发生在编译阶段,而不是链接阶段。编译器需要知道类模板的完整定义(包括实现)才能生成具体的代码。如果实现部分不在头文件中,编译器无法完成模板的实例化。

模板的编译机制

模板类的代码(包括定义和实现)必须在编译时完全可见。这是因为模板类的实例化(instantiation)是基于模板参数(如类型参数)动态生成具体代码的。编译器需要在编译时根据模板参数生成具体的代码,因此模板的定义和实现不能分离

二. 关于容量和大小的函数

2.1 size和capacity

size_t size() const
{return finish - start; //返回容器中有效数据的个数
}
size_t capacity() const
{return end_of_storage - start; //返回容器的最大容量
}

2.2 reserve

reserve规则:
1.当n大于对象当前的容量,就将容量扩容到n。
2.当n小于等于对象当前的容量,就什么都不做。

void reserve(size_t n)
{if (n > capacity()){size_t oldsize = size();T* tmp = new T[n]; //开辟新空间if (start) {memcpy(tmp, start, sizeof(T) * oldsize); //拷贝数据}delete[] start; //释放旧空间start = tmp;finish = start + oldsize;end_of_storage = start + n;}
}

注意在进行扩容操作前需要记录当前容器的有效数据个数

因为要改变finish指针,而finish等于start+有效数据的个数,但是start已经指向新空间了,当试图调用size函数获取有效数据个数时就会返回一个随机值,所以在进行操作之前就要先将容器的有效数据个数记录下来。

2.3 resize

resize规则:
1.当n大于容器的有效数据个数size时,将size扩大到n,用val填充未初始化的数据,如果val没有给出,val默认为容器所存储数据类型的默认值。
2.当n小于容器的有效数据个数size时,将size缩小到n。

void resize(size_t n, const T& val = T())
{if (n < size()){finish = start + n;}else{reserve(n); //检查是否需要扩容while (finish < start + n){*finish = val;finish++;}}
}

注意:在C++中,对于自定义类型需要通过默认构造函数:类型()构造出一个临时对象,val再引用这个临时对象,而对于内置类型也可以通过类型()获得该类型的默认值,这是为了兼并内置类型和自定义类型。

在这里插入图片描述

2.4 empty

判断容器是否为空

bool empty() const
{return start == finish;
}

三. vector的默认成员函数

3.1 构造函数

3.1.1 无参构造函数

对于vector的无参构造函数,直接将三个成员变量都初始化为nullptr即可。

vector():start(nullptr),finish(nullptr),end_of_shorage(nullptr)
{ }

3.1.2 构造初始化为n个val值

构造一个vector容器,该容器的数据为n个val值。先调用reserve函数将容器的容量设置为n,再将n个val插入容器当中即可。

vector(size_t n, const T& val = T())
{reserve(n);for (size_t i = 0; i < n; i++){start[i] = val;}finish = start + n;
}

3.1.3 用initializer_list构造初始化

在 C++ 中,std::initializer_list 是一个模板类,用于表示初始化列表(即用 {} 包围的值列表)。它通常用于支持统一初始化语法,这是 C++11 引入的一种初始化方式。std::initializer_list 提供了一种类型安全的方式来处理初始化列表,并且可以在函数参数、数组初始化、容器初始化等场景中使用。

基本概念

std::initializer_list 是一个轻量级的容器,它存储了初始化列表的开始和结束指针,但不会复制列表中的元素。它的主要目的是提供一种统一的语法来初始化对象或传递参数。

语法

std::initializer_list<T> list = { value1, value2, ..., valuen };

其中 T 是列表中元素的类型。

vector(initializer_list<T> il)

这个构造函数的作用是将 initializer_list< T > 中的元素复制到 vector 的内部存储中。

实现细节

从 initializer_list 中获取元素的迭代器范围(begin() 和 end()),遍历每个元素,将这些元素逐个复制到vector的内部存储中。

注意事项

  1. std::initializer_list 的生命周期与初始化列表的生命周期相同。例如,如果初始化列表是在函数调用中传递的,那么 std::initializer_list 的生命周期只持续到函数调用结束。
  2. std::initializer_list 中的元素是只读的,不能直接修改。如果需要修改,可以先复制到 vector 中,然后对 vector 进行操作。
vector(initializer_list<T> il)
{reserve(il.size());size_t i = 0;for (auto& x : il){start[i++] = x;}finish = start + i; //不要忘记修改finish
}

3.1.4 使用迭代器区间进行构造初始化

为了不止可以用该容器的迭代器区间构造初始化,也可以用其他容器的迭代器区间进行构造初始化,所以我们可以将该构造函数设计为一个函数模板,用模板参数自动推导出迭代器的类型即可。

template<class InputIterator>
vector(InputIterator first, InputIterator last) //左闭右开
{reserve(last - first);size_t i = 0;while (first != last){start[i++] = *first;first++;}finish = start + i;
}

注意:这里有个小bug,如果调用构造函数MyVector::vector< int > vv(5, 1);时,因为构造函数vector(size_t n, const T& val = T())的第一个数n是size_t类型,要调用只能隐式类型转换,而函数模板可以将InputIterator推导为int,两个函数参数都是int类型,无需进行隐式类型转换,那么编译器会选择更加适配的vector(InputIterator first, InputIterator last)构造函数,从而引发报错,那么怎么解决这个问题呢?

可以再写一个第一个函数参数n为int类型的构造函数:vector(int n, const T& val = T())

vector(int n, const T& val = T())
{reserve(n);for (int i = 0; i < n; i++){start[i] = val;}finish = start + n;
}

3.2 拷贝构造函数

思路很简单,先将该容器扩容到和容器v的size一样大小,再将v的数据拷贝过来即可。

vector(const vector<T>& v)
{size_t sz = v.size();reserve(sz);memcpy(start, v.start, sizeof(T) * sz);finish = start + sz;
}

注意:严格来说,将容器v的数据一个一个拷贝过来时不能使用memcpy函数,当vector容器存储的是内置类型或无需进行深拷贝的自定义类型时,可以使用memcpy函数,但是当vector存储的是需要进行深拷贝的自定义类型时,memcpy函数就不适用了,会引发一块空间被多次释放的问题。

例如:当vector存储的是string类型时,情况如下

vector存储的每一个string对象都指向自己所存储的字符串。

如果此时我们使用memcpy函数进行拷贝构造,那么拷贝构造出来的vector存储的string对象的成员变量和被拷贝的vector存储的string对象的成员变量是一模一样的,它们的_str指针都指向同一块内存空间。

当调用vector的析构函数时,从而调用string的析构函数,这样会引起同一块空间被释放多次的问题。

那么如何解决这个问题呢?
当要将vector的string拷贝过来时,应当实现深拷贝,怎样才能实现深拷贝呢?——>string的赋值运算符重载

在这里插入图片描述

这样就实现了深拷贝,避免了一块空间被释放多次的问题。

vector(const vector<T>& v)
{size_t sz = v.size();reserve(sz);for (size_t i = 0; i < sz; i++){start[i] = v.start[i];}finish = start + sz;
}

还有一种现代写法,复用了使用迭代器区间进行构造初始化的构造函数:

vector(const vector<T>& v)
{vector<T> tmp(v.begin(), v.end());std::swap(start, tmp.start);std::swap(finish, tmp.finish);std::swap(end_of_storage, tmp.end_of_storage);
}

我们可以实现一个交换函数:

void swap(vector<T>& v)
{std::swap(start, v.start);std::swap(finish, v.finish);std::swap(end_of_storage, v.end_of_storage);
}
vector(const vector<T>& v)
{vector<T> tmp(v.begin(), v.end());swap(tmp);
}

3.3 赋值运算符重载

vector<T>& operator=(vector<T> v) //调用拷贝构造函数构造出v对象
{swap(v);//交换两个对象return *this; //支持连续赋值
}

注意要使用传值传参,编译器会间接调用拷贝构造函数构造出v对象,然后将该对象与v交换即可,为了支持连续赋值,最后返回该对象,编译器在函数调用结束时会自动调用v的析构函数,将v释放掉。

3.4 析构函数

~vector()
{if (start) { //先判断start为不为空,如果不为空则释放空间,否则无需进行操作delete[] start;}start = finish = end_of_storage = nullptr;
}

析构前先判断start是否为空,如果不为空,则释放指向的空间,否则就无需释放,最后将三个指针start,finish,end_of_storage都指向空即可。

四. iterator迭代器

typedef T* iterator;
typedef const T* const_iterator;

vector迭代器就是原生指针T*,begin函数返回容器第一个元素的地址,即start;end函数返回容器最后一个元素的下一个地址,即finish。

注意:const对象的begin和end函数要用const修饰this指针指向的内容,因为const对象不允许被修改。

iterator begin()
{return start;
}
iterator end()
{return finish;
}
const_iterator begin() const
{return start;
}
const_iterator end() const
{return finish;
}

测试:

void Test()
{MyVector::vector<int> v = { 1,2,3,4,5,6 };MyVector::vector<int>::iterator it = v.begin();while (it != v.end()){cout << *it << " ";it++;}cout << endl;const MyVector::vector<int> v1 = { 1,3,5,7,9 };MyVector::vector<int>::const_iterator it1 = v1.begin();while (it1 != v1.end()){cout << *it1 << " ";it1++;}
}

在这里插入图片描述

因为实现了迭代器,所以支持使用范围for去遍历vector

void Test()
{MyVector::vector<int> v = { 1,2,3,4,5,6 };for (auto& e : v){cout << e << " ";}cout << endl;const MyVector::vector<int> v1 = { 1,3,5,7,9 };for (auto& e : v1){cout << e << " ";}
}

在这里插入图片描述

五. operator[]访问函数

T& operator[](size_t i)
{assert(i < size()); //检查下标是否合法return start[i];
}
const T& operator[](size_t i) const
{assert(i < size()); //检查下标是否合法return start[i];
}

注意:不要忘了重载一个适用于const对象的operator[]函数,因为const对象只允许读,不允许修改。

六. 修改容器内容的相关函数

6.1 push_back

尾插前先判断容器是否已满,如果满了就先扩容再尾插,如果没满直接尾插即可,最后不要忘了将finish向后移一位。

void push_back(const T& x)
{if (finish == end_of_storage) {reserve(capacity() == 0 ? 4 : 2 * capacity());}*finish = x;finish++;
}

测试:

void Test()
{MyVector::vector<string> v;v.push_back("hello world");v.push_back("hello world");v.push_back("hello world");v.push_back("hello world");for (auto& e : v){cout << e << endl;}
}

在这里插入图片描述
运行后感觉是没什么问题,如果此时我再尾插一个相同的数据,会发生什么呢?

void Test()
{MyVector::vector<string> v;v.push_back("hello world");v.push_back("hello world");v.push_back("hello world");v.push_back("hello world");v.push_back("hello world"); //再尾插一个相同的数据for (auto& e : v){cout << e << endl;}
}

可以看到,程序崩溃了,为什么会这样呢?

容器里有四个string对象不会报错,再尾插一个就报错了,那么问题应该是出现在了扩容函数上:

void reserve(size_t n)
{if (n > capacity()){size_t oldsize = size();T* tmp = new T[n]; //开辟新空间if (start) {memcpy(tmp, start, sizeof(T) * oldsize); //拷贝数据}delete[] start; //释放旧空间start = tmp;finish = start + oldsize;end_of_storage = start + n;}
}

可以看到,reserve函数里拷贝数据的方式用的是memcpy函数,也就是浅拷贝(一个字节一个字节地拷贝),但是string是自定义类型,之前提到过,string实现的是深拷贝,所以memcpy函数在这里就不适用了,应当使用string的赋值运算符重载函数。

void reserve(size_t n)
{if (n > capacity()){size_t oldsize = size();T* tmp = new T[n]; //开辟新空间if (start) {//memcpy(tmp, start, sizeof(T) * oldsize); //拷贝数据for (size_t i = 0; i < oldsize; i++){tmp[i] = start[i]; //深拷贝}}delete[] start; //释放旧空间start = tmp;finish = start + oldsize;end_of_storage = start + n;}
}

在这里插入图片描述
再进行测试就没问题了。

6.2 pop_back()

尾删前先判断容器是否为空,如果为空则做断言处理,不为空则将finish指针向前移动一位即可。

void pop_back()
{assert(!empty()); //判断容器是否为空,为空则断言finish--; //将finish指针向前移一位
}

测试:

void Test()
{MyVector::vector<int> v = { 1,2,3,4 };for (auto& e : v){cout << e << " ";}cout << endl;while (!v.empty()){v.pop_back();for (auto& e : v){cout << e << " ";}cout << endl;}
}

在这里插入图片描述

6.3 insert

在pos迭代器的指定位置插入数据x,在插入数据之前先判断是否需要扩容,在pos以及之后的数据向后移动一位,注意是从后往前的顺序,最后在pos位置插入数据x,还有不要忘了将finish向后移动一位,使有效数据个数加一即可。

void insert(iterator pos, const T& x)
{assert(pos >= begin() && pos <= end()); //检查迭代器pos是否合法if (finish == end_of_storage) {//因为要扩容,旧空间会被释放掉,pos迭代器会失效,所以要更新pos迭代器size_t oldsize = pos - start; //记录pos和start之间的长度reserve(capacity() == 0 ? 4 : 2 * capacity()); //扩容pos = start + oldsize; //将pos指向新空间的指定位置}//将pos以及之后的数据向后移动一位iterator it = finish;while (it > pos){*it = *(it - 1);it--;}*pos = x; //更新数据finish++; //finish向后移动一位,有效数据加一
}

测试:

void Test()
{MyVector::vector<int> v = { 1,2,3,4,5,6 };v.insert(v.begin() + 6, 7);v.insert(v.begin() + 2, 8);for (auto& e : v){cout << e << " ";}cout << endl;
}

在这里插入图片描述

6.4 erase

将迭代器pos位置的数据删除,首先检查容器是否为空和pos迭代器是否在[start,finish)区间内,如果有一项不符合就做断言处理,删除数据时将pos之后的数据向前移动一位即可(把pos位置的数据覆盖掉),这里注意是从前往后的顺序。

iterator erase(iterator pos)
{assert(!empty()); //检查容器是否为空assert(pos >= start && pos < finish); //检查迭代器pos是否合法//将pos之后的数据向前移动一位iterator it = pos + 1;while (it < finish){*(it - 1) = *it;it++;}finish--; //finish向前移动一位,有效数据减一return pos; //返回被删除数据的下一个数据的迭代器
}

测试:

void Test()
{MyVector::vector<int> v = { 1,2,2,3,4,5,6,7,8 };auto it = v.begin();while (it != v.end()){if (*it % 2 == 0) {//v.erase(it)之后it迭代器就失效了,所以要让it指向被删除数据位置的下一个迭代器it = v.erase(it);}else {it++;}}for (auto& e : v){cout << e << " ";}cout << endl;
}

在这里插入图片描述

6.5 clear

将容器内存储的数据清空。

void clear()
{finish = start;
}

测试:

void Test()
{MyVector::vector<int> v = { 1,2,3,4,5,6 };for (auto& e : v){cout << e << " ";}cout << endl;v.clear();for (auto& e : v){cout << e << " ";}cout << endl;
}

在这里插入图片描述

七. 源代码

vector.h文件

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<assert.h>
#include<vector>
using namespace std;
//用命名空间MyVector封装vector,避免与std库的vector冲突
namespace MyVector
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;size_t size() const{return finish - start; //返回容器中有效数据的个数}size_t capacity() const{return end_of_storage - start; //返回容器的最大容量}void reserve(size_t n){if (n > capacity()){size_t oldsize = size();T* tmp = new T[n]; //开辟新空间if (start) {//memcpy(tmp, start, sizeof(T) * oldsize); //拷贝数据for (size_t i = 0; i < oldsize; i++){tmp[i] = start[i]; //深拷贝}}delete[] start; //释放旧空间start = tmp;finish = start + oldsize;end_of_storage = start + n;}}void resize(size_t n, const T& val = T()){if (n < size()){finish = start + n;}else{reserve(n); //检查是否需要扩容while (finish < start + n){*finish = val;finish++;}}}bool empty() const{return start == finish;}iterator begin(){return start;}iterator end(){return finish;}const_iterator begin() const{return start;}const_iterator end() const{return finish;}vector():start(nullptr),finish(nullptr),end_of_storage(nullptr){}vector(size_t n, const T& val = T()){reserve(n);for (size_t i = 0; i < n; i++){start[i] = val;}finish = start + n;}vector(int n, const T& val = T()){reserve(n);for (int i = 0; i < n; i++){start[i] = val;}finish = start + n;}vector(initializer_list<T> il){reserve(il.size());size_t i = 0;for (auto& x : il){start[i++] = x;}finish = start + i; //不要忘记修改finish}template<class InputIterator>vector(InputIterator first, InputIterator last) //左闭右开{reserve(last - first);size_t i = 0;while (first != last){start[i++] = *first;first++;}finish = start + i;}/*vector(const vector<T>& v){size_t sz = v.size();reserve(sz);memcpy(start, v.start, sizeof(T) * sz);finish = start + sz;}*//*vector(const vector<T>& v){size_t sz = v.size();reserve(sz);for (size_t i = 0; i < sz; i++){start[i] = v.start[i];}finish = start + sz;}*/void swap(vector<T>& v){std::swap(start, v.start);std::swap(finish, v.finish);std::swap(end_of_storage, v.end_of_storage);}vector(const vector<T>& v){vector<T> tmp(v.begin(), v.end());swap(tmp);}vector<T>& operator=(vector<T> v) //调用拷贝构造函数构造出v对象{swap(v);//交换两个对象return *this; //支持连续赋值}~vector(){if (start) { //先判断start为不为空,如果不为空则释放空间,否则无需进行操作delete[] start;}start = finish = end_of_storage = nullptr;}T& operator[](size_t i){assert(i < size()); //检查下标是否合法return start[i];}const T& operator[](size_t i) const{assert(i < size()); //检查下标是否合法return start[i];}void push_back(const T& x){if (finish == end_of_storage) {reserve(capacity() == 0 ? 4 : 2 * capacity());}*finish = x;finish++;}void pop_back(){assert(!empty()); //判断容器是否为空,为空则断言finish--; //将finish指针向前移一位}void insert(iterator pos, const T& x){assert(pos >= begin() && pos <= end()); //检查迭代器pos是否合法if (finish == end_of_storage) {//因为要扩容,旧空间会被释放掉,pos迭代器会失效,所以要更新pos迭代器size_t oldsize = pos - start; //记录pos和start之间的长度reserve(capacity() == 0 ? 4 : 2 * capacity()); //扩容pos = start + oldsize; //将pos指向新空间的指定位置}//将pos以及之后的数据向后移动一位iterator it = finish;while (it > pos){*it = *(it - 1);it--;}*pos = x; //更新数据finish++; //finish向后移动一位,有效数据加一}iterator erase(iterator pos){assert(!empty()); //检查容器是否为空assert(pos >= start && pos < finish); //检查迭代器pos是否合法//将pos之后的数据向前移动一位iterator it = pos + 1;while (it < finish){*(it - 1) = *it;it++;}finish--; //finish向前移动一位,有效数据减一return pos; //返回被删除数据的下一个数据的迭代器}void clear(){finish = start;}private:iterator start; //指向数据块的开始iterator finish; //指向有效数据的末尾iterator end_of_storage; //指向存储容量的末尾};
}

END

对以上内容有异议或者需要补充的,欢迎大家来讨论!

相关文章:

【C++】vector的模拟实现

文章目录 前言一. vector的底层二. 关于容量和大小的函数2.1 size和capacity2.2 reserve2.3 resize2.4 empty 三. vector的默认成员函数3.1 构造函数3.1.1 无参构造函数3.1.2 构造初始化为n个val值3.1.3 用initializer_list构造初始化3.1.4 使用迭代器区间进行构造初始化 3.2 拷…...

C# Winform 入门(9)之如何封装并调用dll

封装dll 首先创建 .Net平台 类库 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace _09.Encapsulation_dll {public class Program{/// <summary>/// 求两个double类型的数值的和/// &l…...

【C语言】内存函数

大家好&#xff0c;很高兴又和大家见面了&#xff01;&#xff01;&#xff01; 在C语言标准库中&#xff0c;有一些直接对内存进行操作的函数&#xff0c;我们将其称之为内存函数&#xff0c;这些函数位于头文件<string.h>&#xff0c;在网站https://cplusplus.com/ref…...

SDL视频显示函数

文章目录 1. **`SDL_Init()`**2. **`SDL_CreateWindow()`**3. **`SDL_CreateRenderer()`**4. **`SDL_CreateTexture()`**5. **`SDL_UpdateTexture()`**6. **`SDL_RenderCopy()`**7. **`SDL_RenderPresent()`**8. **`SDL_Delay()`**9. **`SDL_Quit()`**总结示例代码:代码说明:…...

博客文章:深入分析 PyMovie - 基于 Python和 MoviePy 的视频管理工具

这是一个使用 wxPython 构建界面、moviepy 处理视频的自定义 GUI 应用程序。该工具提供了视频播放、元数据提取、格式转换、视频裁剪和截图等功能。通过分析其设计和实现&#xff0c;我们将了解其工作原理、优点和潜在的改进空间。 C:\pythoncode\new\output\pymovieSample.py …...

Redis中AOF的实现方式和AOF重写

一、AOF 的实现方式 核心原理 AOF 通过将写操作命令以追加方式记录到日志文件中&#xff0c;重启时通过重放命令恢复数据。与 RDB 的快照机制不同&#xff0c;AOF 是增量记录&#xff0c;更适用于数据一致性要求较高的场景。写入流程 命令执行&#xff1a;客户端发送写命令&am…...

Supervisor的安装和使用

Supervisor 使用笔记&#xff08;CentOS 8 环境&#xff09; 本周&#xff0c;老师让我使用supervisor管理项目服务&#xff0c;当时第一次听说过这个进程管理工具&#x1f636;‍&#x1f32b;️&#xff0c;就上网搜了搜安装和使用&#xff0c;又用ai查了一些细节&#xff0…...

JVM 内存区域详解

JVM 内存区域详解 Java 虚拟机&#xff08;JVM&#xff09;的内存区域划分为多个部分&#xff0c;每个部分有特定的用途和管理机制。以下是 JVM 内存区域的核心组成及其功能&#xff1a; 一、运行时数据区&#xff08;Runtime Data Areas&#xff09; 1. 线程共享区域 内存…...

【java】在 Java 中,获取一个类的`Class`对象有多种方式

在 Java 中&#xff0c;获取一个类的Class对象有多种方式。Class对象代表了 Java 中的一个类或接口的运行时类信息&#xff0c;可以用于反射操作。以下是获取Class对象的几种常见方法&#xff1a; 1.使用.class属性 每个类都有一个.class属性&#xff0c;可以直接获取该类的Cl…...

蓝桥杯:对字符串处理常用知识笔记

一、前面四个是计算带有空格字符串的的长度计算 C语言代码 #include<string.h> #include<stdio.h> int main() { char s[105]; gets(s); printf("%d", strlen(s)); return 0; } 算法2 C 代码&#xff08;常用&#xff09; #include <iostream> #in…...

c++网络编程,信号透传可能是什么意思

在 C 网络编程中&#xff0c;**信号透传**&#xff08;Signal Pass-Through&#xff09;通常 refers to the concept of allowing signals to propagate through a network protocol stack without being interrupted or modified. 具体来说&#xff0c;信号透传可以涉及到几个…...

数据结构与算法学习笔记----贪心·绝对值不等式

数据结构与算法学习笔记----贪心绝对值不等式 author: 明月清了个风 first publish time: 2025.4.5 ps⭐️感觉其实是一个数学的问题&#xff0c; Acwing 104. 货仓选址 [原题链接](104. 货仓选址 - AcWing题库) 在一条数轴上有 N N N家商店&#xff0c;他们的坐标分别为 A…...

CUDA学习--体验GPU性能

学习来源&#xff1a;2 CUDA Python--并行计算基础-卷积计算以及共享内存_哔哩哔哩_bilibili 处理一张图片的处理速度对比 import cv2 from numba import cuda import time import math cuda.jit() def process_gpu(img,channels):tx cuda.blockIdx.x*cuda.blockDim.xcuda…...

博途 TIA Portal之1200做主站与200SMART的S7通讯

有时候,我们与之作S7通讯的西门子系PLC并不是同一个厂商或是同一时期供货的,也有可能不在一个编程软件中。此时进行S7能讯会有所不同。本文将演示博途与200SMART做S7通讯。 1、硬件准备 1200PLC一以,200SMART一台,网线2根,交换机一台。 2、关于编程 1200做主站,因此需…...

a标签download下载图片

‌a标签的download属性是HTML5中新增的一个属性&#xff0c;用于指定链接点击时直接下载文件&#xff0c;而不是在浏览器中打开文件。‌ 基本用法 ‌指定下载文件名‌&#xff1a;在a标签中添加download属性&#xff0c;并指定一个文件名。例如&#xff1a; <a href"…...

#SVA语法滴水穿石# (013)关于 disable iff、matched 、expect 的用法

SystemVerilog 断言&#xff08;SVA&#xff09;中 disable iff、matched 和 expect 的语法知识。 1. disable iff (condition) 功能与定义 作用&#xff1a;当指定条件&#xff08;condition&#xff09;为真时&#xff0c;禁用当前属性的检查。 常用于复位&#xff08;rese…...

Day2-2:前端项目uniapp壁纸实战

再在wallpaper新建一个目录components 在components下新建组件common-title 记得点击创建同名目录 在index加 <view class"select"><common-title></common-title></view> 图片换了下&#xff0c;原来的有点丑&#xff0c;图片可按自己喜欢…...

pycharm如何通过跳板机连接服务器在本地debug

现在假设你有一个服务器&#xff0c;需要跳板机登陆&#xff0c;但是你从跳板机到服务器&#xff0c;只知道能直接通过ssh连接。 首先你可以现在本地创建一个 SSH 配置文件&#xff08;~/.ssh/config&#xff09;&#xff1a; Host jumpHostName 跳板机地址Port 端口User 用户…...

Mysql explain中列的解析

EXPLAIN列的解释&#xff1a; table&#xff1a;显示这一行的数据是关于哪张表的 type&#xff1a;这是重要的列&#xff0c;显示连接使用了何种类型。从最好到最差的连接类型为const、eq_reg、ref、range、indexhe和ALL possible_keys&#xff1a;查询可以利用的索引&#…...

场馆预定系统小程序PHP+uniapp

场馆预定系统小程序&#xff1a;基于PHPUniApp的多场景体育场馆智慧化解决方案 随着全民健身意识的提升&#xff0c;体育场馆的数字化管理需求日益增长。场馆预定系统小程序凭借其轻量化、高便捷性&#xff0c;成为体育馆、羽毛球馆、兵乒球馆等场所提升运营效率的核心工具。本…...

05.unity 游戏开发-3D工程的创建及使用方式和区别

05.unity 游戏开发-3D工程的创建及使用方式和区别 提示&#xff1a;帮帮志会陆续更新非常多的IT技术知识&#xff0c;希望分享的内容对您有用。本章分享的是Python基础语法。前后每一小节的内容是存在的有&#xff1a;学习and理解的关联性&#xff0c;希望对您有用~ unity简介…...

php8 命名参数使用教程

简介 PHP 8 引入 命名参数&#xff08;Named Arguments&#xff09;&#xff0c;允许在调用函数时按参数名传递值&#xff0c;而不是按照参数位置。这增强了代码的可读性、灵活性&#xff0c;并减少参数顺序依赖。 基本用法 传统位置参数&#xff08;Positional Arguments&a…...

Transformer与注意力机制详解

1 Transformer与注意力机制详解 本文直观上详细介绍了大语言模型中十分重要的结构——Transformer,及其核心:注意力机制的原理。 1. Transformer结构 基础结构如下图所示,左侧由一系列Encoder block(编码器)构成,接收字词句输入;右侧由一系列Decoder block(解码器)…...

xLua环境控制+xLua的Lua调用C#的1

编写自定义加载器加载指定路径的Lua文件&#xff1a; using System.Collections; using System.Collections.Generic; using System.IO; using UnityEngine; using XLua;//Lua是脚本语言&#xff0c;编写代码脚本是实现功能最重要的方式 public class Loader : MonoBehaviour …...

RabbitMQ应用问题

RabbitMQ应用问题 一.幂等性1.简述概念2.MQ的幂等性介绍3.解决幂等性问题(1)全局唯一ID(2)业务逻辑判断 二.顺序性保障1.简单介绍2.无法保证顺序性的场景3.保障方案1)单队列消费者2)分区消费3)消息确认机制4)业务逻辑控制 三.消息积压问题1.原因分析2.解决方案 一.幂等性 1.简…...

轻量化大模型微调工具XTuner指令微调实战(下篇)

接着上篇文章《轻量化大模型微调工具XTuner指令微调实战&#xff08;上篇&#xff09;》来接着写教程。 一、模型转换 模型训练后会自动保存成 PTH 模型&#xff08;例如 iter_500.pth&#xff09;&#xff0c;我们需要利用 xtuner convert pth_to_hf 将其转换为 HuggingFace…...

Redis数据结构之ZSet

目录 1.概述2.常见操作2.1 ZADD2.2 ZRANGE2.3 ZREVRANGE2.4 ZRANGEBYSCORE2.5 ZSCORE2.6 ZCARD2.6 ZREM2.7 ZINCRBY2.8 ZCOUNT2.9 ZMPOP2.10 ZRANK2.11 ZREVRANK 3.总结 1.概述 ZSet和Set一样也是String类型元素的集合&#xff0c;且不允许重复的成员&#xff0c;不同的是ZSet…...

STM32提高篇: CAN通讯

STM32提高篇: CAN通讯 一.CAN通讯介绍1.物理层2.协议层二.STM32CAN外设1.CAN控制器的3种工作模式2.CAN控制器的3种测试模式3.功能框图三.CAN的寄存器介绍1.环回静默模式测试2.双击互发测试四.CAN的HAL代码解读一.CAN通讯介绍 CAN(Controller Area Network 控制器局域网,简称…...

贪心算法之最小生成树问题

1. 贪心算法的基本思想 贪心算法在每一步都选择局部最优的边&#xff0c;希望最终得到整体最优的生成树。常见的两种 MST 算法为 Kruskal 算法 和 Prim 算法。这两者均满足贪心选择性质和最优子结构性质&#xff0c;即&#xff1a; 贪心选择性质&#xff1a;局部最优选择&…...

C++EasyX之五子棋PVP和PVE

以下是该C EasyX五子棋代码的详细解析&#xff1a; ​ 1 代码 1.1 全代码 #include <graphics.h> #include <conio.h> #include <Windows.h> #include <cmath> #include <vector> #include <tuple> #include <algorithm>using na…...

【Tauri2】015——前端的事件、方法和invoke函数

目录 前言 正文 准备 关键url 获取所有命令 切换主题set_theme 设置大小 获得版本version 名字name 监听窗口移动 前言 【Tauri2】005——tauri::command属性与invoke函数-CSDN博客https://blog.csdn.net/qq_63401240/article/details/146581991?spm1001.2014.3001.…...

【C++奇遇记】C++中的进阶知识(继承(二))

&#x1f3ac; 博客主页&#xff1a;博主链接 &#x1f3a5; 本文由 M malloc 原创&#xff0c;首发于 CSDN&#x1f649; &#x1f384; 学习专栏推荐&#xff1a;LeetCode刷题集 数据库专栏 初阶数据结构 &#x1f3c5; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如…...

qt designer 软件主题程序设计

对于使用Qt Designer设计的界面&#xff0c;主题切换的实现需要结合Qt的信号槽机制、样式表动态加载以及资源管理。以下是针对Qt Designer UI的详细解决方案&#xff1a; 一、UI文件与主题系统的整合架构 二、核心实现步骤 1. 动态样式表加载系统 // ThemeManager.h class …...

2025 年 4 月补丁星期二预测:微软将推出更多 AI 安全功能

微软正在继续构建其 AI 网络安全战略&#xff0c;并于本月宣布在 Microsoft Security Copilot 中引入新代理。 他们引入了用于网络钓鱼分类的代理、用于数据丢失预防和内部风险管理的警报分类、条件访问优化、漏洞修复和威胁情报简报。 这些代理的目标是不断从这些不同学科中…...

docker swarm常用命令

1、初始化Swarm集群 用于初始化一个Swarm集群&#xff0c;并将当前节点设置为Manager节点。 用法&#xff1a;docker swarm init --advertise-addr <Manager节点IP> # docker swarm init --advertise-addr 192.168.1.100 这会将当前节点初始化为Swarm集群的管理节点&…...

抖音直播位置与IP属地不同?解析原因与应对策略

在当今短视频直播盛行的时代&#xff0c;抖音作为头部平台吸引了大量主播和观众。然而&#xff0c;许多用户发现一个令人困惑的现象&#xff1a;直播间显示的位置信息与账号IP属地不一致。这种情况不仅让观众产生疑问&#xff0c;也可能给主播带来不必要的麻烦。本文将深入分析…...

「限时开源」全网首发!DeepSeek-R1+AI绘画+音乐生成全栈源码

—企业级AIGC私有化终极方案&#xff0c;3大模态整合&#xff0c;成本直降90% 行业痛点&#xff1a;为什么企业急需这套方案&#xff1f; 1. 多模态AIGC的三大困局 成本失控 API吸血&#xff1a; 使用MidjourneyStable DiffusionGPT-4Suno API生成内容&#xff0c;企业月均支…...

设计模式简述(二)单例模式

单例模式 描述基本使用防破坏单例饿汉式懒汉式有上限多例 描述 一个类全局只有一个实例&#xff0c;自行实例化并提供给使用。 构造函数私有化是前提 基本使用 防破坏单例 防反射&#xff1a;在构造函数中判断类中实例是否已初始化 private InnerClassSingleton (){if(Inn…...

区块链钱包:与主流钱包APP的区别

前言 在前端开发者速入:DApp中的前端要干些什么?文中我简单讲解了在DApp中前端开发者要干的是什么,本来在接下来的内容中我应该继续讲解在DApp中前端开发者的一系列工作和其他所要用到的技术栈,但是为了方便后续的讲解,我们这里不得不提及一下在区块链中让无数人又爱又恨…...

23种设计模式-行为型模式-中介者

文章目录 简介问题解决代码架构优势 总结 简介 中介者是一种行为设计模式&#xff0c; 能让你减少对象之间混乱无序的依赖关系。该模式会限制对象之间的直接交互&#xff0c;强制让它们通过一个中介者对象进行合作。 问题 假如你有一个创建和修改用户资料的对话框&#xff0…...

mysql数据库中getshell的方式总结

mysql数据库中getshell的方式总结 MySQL版本大于5.0&#xff0c;MySQL 5.0版本以上会创建日志文件,我们通过修改日志文件的全局变量,就可以GetSHELL,下面这篇文章主要给大家介绍了关于mysql数据库中getshell的方式,需要的朋友可以参考下 outfile和dumpfile写shell 利用条件 …...

神经网络基础

神经网络的基本组成元素 一个神经元&#xff1a; 单层神经网络&#xff1a; 多层神经网络&#xff1a;&#xff08;前向计算&#xff09; 为什么要使用激活函数 如果不使用激活函数&#xff0c;每层只对上层的输入进行线性变换&#xff0c;实际这些线性变换可以归为一层即可。…...

Redis的公共操作命令

目录 1.Key操作命令1.1 keys *1.2 exists <key]>1.3 type <key>1.4 del <key>1.5 unlink <key>1.6 ttl <key>1.7 expire <key> <秒数>1.8 move <key> <index> 2.库操作命令2.1 select <index>2.2 dbsize2.3 flush…...

Redash:一个开源的数据查询与可视化工具

Redash 是一款免费开源的数据可视化与协作工具&#xff0c;可以帮助用户快速连接数据源、编写查询、生成图表并构建交互式仪表盘。它简化了数据探索和共享的过程&#xff0c;尤其适合需要团队协作的数据分析场景。 数据源 Redash 支持各种 SQL、NoSQL、大数据和 API 数据源&am…...

Transformer+BO-SVM多变量时间序列预测(Matlab)

TransformerBO-SVM多变量时间序列预测&#xff08;Matlab&#xff09; 目录 TransformerBO-SVM多变量时间序列预测&#xff08;Matlab&#xff09;效果一览基本介绍程序设计参考资料 效果一览 基本介绍 本期推出一期高创新模型&#xff0c;基于Transformer提取时序特征后输入S…...

【Redis】服务端高并发分布式结构

一、认识Redis Redis是开源的&#xff0c;用于存储数据的&#xff0c;在内存中存储数据。Redis被用作数据库&#xff0c;缓存&#xff0c;消息队列等一些作用。 在数据库的学习中&#xff0c;我们学习了MySQL&#xff0c;但是MySQL有一些大问题&#xff1a;其访问速度比较慢。在…...

C和C++(list)的链表初步

链表是构建其他复杂数据结构的基础&#xff0c;如栈、队列、图和哈希表等。通过对链表进行适当的扩展和修改&#xff0c;可以实现这些数据结构的功能。想学算法&#xff0c;数据结构&#xff0c;不会链表是万万不行的。这篇笔记是一名小白在学习时整理的。 C语言 链表部分 …...

第九课:LoRA模型的原理及应用

文章目录 Part.01 3种LoRA的使用方式Part.02 5种LoRA的应用方向Part.01 3种LoRA的使用方式 LoRA能够在家用级设备上训练,实现对Checkpoint在某些方面的微调使用Lora的三种方式:放置Lora模型到目录中,然后作为提示词的一部分输入。点击生成按钮下面的“画”,然后打开Additio…...

make_01_Program_07_$@ $^ 是什么含义

在 Makefile 中&#xff0c;$ 和 $^ 是自动变量&#xff0c;用于表示目标和依赖关系的特殊含义。它们常用于规则中的命令部分&#xff0c;以便简化 Makefile 的书写。 自动变量解析 $&#xff1a; $ 表示当前规则的目标文件名。换句话说&#xff0c;在一个特定的规则中&#x…...

CKPT文件是什么?

检查点&#xff08;Checkpoint&#xff0c;简称ckpt&#xff09;是一种用于记录系统状态或数据变化的技术&#xff0c;广泛应用于数据库管理、机器学习模型训练、并行计算以及网络安全等领域。以下将详细介绍不同领域中ckpt检查点的定义、功能和应用场景。 数据库中的ckpt检查点…...