C++ STL vector 模拟实现
✅<1>主页:我的代码爱吃辣
📃<2>知识讲解:C++之STL
🔥<3>创作者:我的代码爱吃辣
☂️<4>开发环境:Visual Studio 2022
💬<5>前言:上次我们已经数字会用了vector,这次我们对其底层更深一步挖掘,其中重点是,Vector中一些深浅拷贝问题。
目录
一.Vector模拟实现的整体框架
二. Vector的构造与析构
三.size(),capacity()
四.reserve(),resize()
1.reserve()
2.resize
五.push_back(),pop_back()
1.push_back()
2. pop_back()
六.Vector的迭代器
七.operator [ ]
八.insert(),erase()
1.迭代器失效
2.insert()
3.erase()
九.再看Vector构造函数
十.拷贝构造
1.深浅拷贝
2.正确的拷贝构造代码:
3.正确的 reserve()
4.赋值运算符重载
十一.总体代码
一.Vector模拟实现的整体框架
我们先认识一下Vector的整体模拟实现框架,Vector在功能上就是我们数据结构阶段实现的顺序表基本一致,但是Vector在成员框架上与顺序表有所不同,且Vector使用类和对象封装支持模板泛型。
template<class T>
class Vector
{
public://迭代器类型typedef T* iterator;typedef const T* const_iterator;//...private:iterator _start;//数据存储首地址iterator _finish;//有效数据尾部地址下一个地址。iterator _end_of_storage;//容量尾地址下一个地址
};
Vector的迭代器是对顺序表原生类型指针的封装。
二. Vector的构造与析构
Vector主要是对成员变量初始化:
Vector():_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){}
析构主要释放我们申请的空间:
~Vector(){delete[] _start;_start = _finish = _end_of_storage = nullptr;}
三.size(),capacity()
size()返回当前顺序表存储的数据个数,capacity()返回当前顺序表的容量。
size_t size() const{return _finish - _start;}size_t capacity() const{return _end_of_storage - _start;}

四.reserve(),resize()
1.reserve()
设置Vector的容量,注意容量支持增加,但是不支持减小。
void reserve(size_t capa){//仅支持容量扩大,不支持容量减小if (capacity() < capa){size_t sz = size();iterator tmp = new T[capa];//分清当前的是否已经有了容量,如果已经有了容量需要释放之前的容量,//如果之前没有容量仅需,将新开的空间指向我们的_start.if (_start){memcpy(tmp, _start, sizeof(T) * capacity()); /*error !!*/delete[] _start;}//注意:此处不能直接tmp+size()来计算,因为在计算_start的时候已经已经改变了_start,//然后计算的size也并非是,准确的size。_start = tmp;_finish = tmp + sz;_end_of_storage = _start + capa;}}
注意:
此处不能直接tmp+size()来计算,因为在计算_start的时候已经已经改变了_start,然后计算的size也并非是,准确的size。除此之外这份代码依旧是有问题的。我们后面解释。
错误代码如下:
void reserve(size_t capa){if (capacity() < capa){iterator tmp = new T[capa];if (_start){memcpy(tmp, _start, sizeof(T) * capacity());delete[] _start;}//注意:此处不能直接tmp+size()来计算,因为在计算_start的时候已经已经改变了_start,//然后计算的size也并非是,准确的size。_start = tmp;_finish = tmp + size();_end_of_storage = _start + capa;}}

2.resize
提供改变存储数据的个数的能力。如果 n < size 时就是删除数据,n > size且空间不够时需要扩容+初始化,空间足够,仅需要初始化剩下的空间。
void resize(size_t n, T val = T()){ //1.n < size;-->删除数据if (n < size()){_finish = _start + n;}//2.n > sizeelse {//(1)如果空间不足,需要扩容+初始化if (n >= capacity()){reserve(n);}//(2)空间足够,仅需要初始化剩下的空间while (_finish != _start + n){*(_finish) = val;_finish++;}}}
五.push_back(),pop_back()
1.push_back()
从尾部插入一个数据。
void push_back(const T& val){//检查是否需要扩容if (_finish == _end_of_storage){capacity() == 0 ? reserve(5) : reserve(capacity() * 2);}//插入数据*(_finish) = val;_finish++;}
2. pop_back()
bool empty() const {return size() == 0;}void pop_back(){//判空assert(!empty());//我们仅需将维护尾部数据的指针向前挪一位。_finish--;}
六.Vector的迭代器
typedef T* iterator;typedef const T* const_iterator;
Vector底层就是顺序存储的结构,所以可以使用原生指针作为迭代器。
//普通迭代器iterator begin(){return _start;}iterator end(){return _finish;}//const 迭代器const_iterator begin()const {return _start;}const_iterator end()const{return _finish;}
有了迭代器就可以支持迭代器访问,和范围for。
int main()
{Vector<int> v1;v1.push_back(100);v1.push_back(200);v1.push_back(300);v1.push_back(400);v1.push_back(500);v1.push_back(600);v1.push_back(700);Vector<int>::iterator it = v1.begin();while (it != v1.end()){cout << *it << " ";it++;}cout << endl;for (auto e : v1){cout << e << " ";}return 0;
}

七.operator [ ]
//穿引用返回T& operator[](size_t pos){//判断位置的合法性assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}

八.insert(),erase()
1.迭代器失效
在模拟实现之前我们先看一下什么是迭代器失效问题:
迭代器失效问题通常发生在容器类的成员函数中,例如erase和insert。在这些函数中,迭代器被重置或修改,导致原始迭代器不再指向容器的正确位置,从而导致迭代器失效。
int main()
{vector<int> v1;v1.push_back(100);v1.push_back(200);v1.push_back(300);v1.push_back(400);v1.push_back(500);v1.push_back(600);v1.push_back(700); vector<int>::iterator pos = find(v1.begin(), v1.end(),200);//对pos位置插入v1.insert(pos, 150);//pos已经失效v1.insert(pos, 170);return 0;
}

原理图:
情况一:

上述代码中的迭代器失效问题也是属于这种情况。
情况二:

2.insert()
iterator insert(iterator pos,T val){assert(pos >= _start);assert(pos < _finish);//迭代器失效问题,记录pos的相对位置int len = pos - _start;if (_finish == _end_of_storage){capacity() == 0 ? reserve(5) : reserve(capacity() * 2);}//扩容后重新计算pos,没有发生扩容pos不变pos = _start + len;iterator end = _finish;//数据挪动while (end >= pos){(*end) = *(end - 1);end--;}_finish++;(*pos) = val;return pos;}
在使用pos时要注意扩容会使得pos失效,需要重新计算pos位置。
3.erase()
iterator erase(iterator pos){//判断位置是否合法assert(pos >= _start);assert(pos < _finish);iterator end = pos ;/挪动数据删除while (end < _finish){*end = *(end + 1);end++;}_finish--;return pos;}
九.再看Vector构造函数
std中的vector还支持使用指定个数和初始化值初始化,和迭代器区间初始化。这两个功能在我们平时也是能用到的。
//1.Vector<T> v(5,10);创建一个Vector并且初始化前5个值为10Vector(size_t n, const T& val = T()):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(n);for (int i = 0; i < n; i++){push_back(val);}}//2.迭代器初始化,[frist,lest)template<class InputIterator>Vector(InputIterator frist, InputIterator lest):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(lest - frist);while (frist != lest){push_back(*frist);frist++;}}//3.防止构造函数调用错误Vector(int n, const T& val = T()):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(n);for (int i = 0; i < n; i++){push_back(val);}}
第三个构造函数的作用是防止构造调用错误冲突,在我们进行如下的调用时:
Vector<int> v2(5, 10);
编译器会以为我们在调用迭代器区间初始化构造函数,因为经过模板的推导,只有迭代器区间初始化构造函数,更适合这个调用。然后将一个整形当作地址在迭代器区间初始化构造函数里面解引用了,报错是:非法的间接寻址。

正常调用结果:
十.拷贝构造
今天这里编译器默认生成的拷贝构造显然是不能用了。
1.深浅拷贝
万万不可以直接使用拷贝函数按二进制或者按字节直接拷贝了。
错误代码1:
Vector(const Vector<T>& v):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(v.capacity());//万万不可以直接按二进制拷贝memcpy(_start, v._start, sizeof(T) * v.capacity()); /*error!!!!*/_finish = _start + v.size();_end_of_storage = _start + v.capacity();}
原因:
调用处代码:
int main()
{string str("abcdefg");Vector<string> v2(5,str);Vector<string> v3(v2);return 0;
}

会使得我们同一块空间被delete两次从而引发内存错误。
2.正确的拷贝构造代码:
Vector(const Vector<T>& v):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(v.capacity());//这里我们将数据一个一个push进去,这样我们借助push_back底层插入的时候,//会使用string的赋值构造,完成深拷贝。for (int i = 0; i < v.size(); i++){push_back(v[i]);}}//现代写法Vector(const Vector<T>& v):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){Vector<T> tmp(v.begin(), v.end());swap(tmp);}
错误代码2:reserve()
void reserve(size_t capa){//仅支持容量扩大,不支持容量减小if (capacity() < capa){size_t sz = size();iterator tmp = new T[capa];//分清当前的是否已经有了容量,如果已经有了容量需要释放之前的容量,//如果之前没有容量仅需,将新开的空间指向我们的_start.if (_start){//这里千万不能按二进制直接拷贝.memcpy(tmp, _start, sizeof(T) * capacity()); /*error !!*/delete[] _start;}//注意:此处不能直接tmp+size()来计算,因为在计算_start的时候已经已经改变了_start,//然后计算的size也并非是,准确的size。_start = tmp;_finish = tmp + sz;_end_of_storage = _start + capa;}}
这里我们仍然是使用了memcpy。
调用处代码:
int main()
{string str("abcdefg");Vector<string> v2;for (int i = 0; i < 6; i++){v2.push_back(str);}return 0;
}

3.正确的 reserve()
void reserve(size_t capa){//仅支持容量扩大,不支持容量减小if (capacity() < capa){size_t sz = size();iterator tmp = new T[capa];//分清当前的是否已经有了容量,如果已经有了容量需要释放之前的容量,//如果之前没有容量仅需,将新开的空间指向我们的_start.if (_start){//这里千万不能按二进制直接拷贝.//memcpy(tmp, _start, sizeof(T) * capacity()); /*ror !!*/for (int i = 0; i < size(); i++){//=内置类型直接赋值,自定义类型使用赋值构造tmp[i]=_start[i];}delete[] _start;}//注意:此处不能直接tmp+size()来计算,因为在计算_start的时候已经已经改变了_start,//然后计算的size也并非是,准确的size。_start = tmp;_finish = tmp + sz;_end_of_storage = _start + capa;}}
这里有一个细节就是在reserve和拷贝构造的拷贝数据的时候我们都是使用了赋值。问题我们并没有重载赋值运算符,编译器自动生成,简单来说就是这里又会是一个浅拷贝。
4.赋值运算符重载
//传统写法Vector<T>& operator=(const Vector<T>& v){T* tmp = new T[v.capacity()];if (_start){for (int i = 0; i < v.size(); i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + v.size();_end_of_storage = _start + v.capacity();return *this;}//现代写法void swap(Vector<T>& v ){std::swap(v._start, _start);std::swap(v._finish, _finish);std::swap(v._end_of_storage, _end_of_storage);}Vector<T>& operator=(Vector<T> v){swap(v);return *this;}
现代写法利用,拷贝构造拷贝出来的对象,然后交换对象的成员。
十一.总体代码
#pragma once
#include<iostream>
#include<algorithm>
#include<string>
#include<cstring>
#include<cassert>
using namespace std;template<class T>
class Vector
{
public:typedef T* iterator;typedef const T* const_iterator;Vector():_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){}//1.Vector<T> v(5,10);创建一个Vector并且初始化前5个值为10Vector(size_t n, const T& val = T()):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(n);for (int i = 0; i < n; i++){push_back(val);}}//2.迭代器初始化,[frist,lest)template<class InputIterator>Vector(InputIterator frist, InputIterator lest):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(lest - frist);while (frist != lest){push_back(*frist);frist++;}}//3.防止构造函数调用冲突Vector(int n, const T& val = T()):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){reserve(n);for (int i = 0; i < n; i++){push_back(val);}}//传统写法 拷贝构造//Vector(const Vector<T>& v)// :_start(nullptr),// _finish(nullptr),// _end_of_storage(nullptr)//{// reserve(v.capacity());// //这里我们将数据一个一个push进去,这样我们借助push_back底层插入的时候,// //会使用string的赋值构造,完成深拷贝。// for (int i = 0; i < v.size(); i++)// {// _start[i] = v[i];// }//}//现代写法,拷贝构造Vector(const Vector<T>& v):_start(nullptr),_finish(nullptr),_end_of_storage(nullptr){Vector<T> tmp(v.begin(), v.end());swap(tmp);}//传统写法,赋值拷贝//Vector<T>& operator=(const Vector<T>& v)//{// T* tmp = new T[v.capacity()];// if (_start)// {// for (int i = 0; i < v.size(); i++)// {// tmp[i] = _start[i];// }// delete[] _start;// }// _start = tmp;// _finish = _start + v.size();// _end_of_storage = _start + v.capacity();// // return *this;//}void swap(Vector<T>& v ){std::swap(v._start, _start);std::swap(v._finish, _finish);std::swap(v._end_of_storage, _end_of_storage);}//现代写法,赋值拷贝Vector<T>& operator=(Vector<T> v){swap(v);return *this;}size_t size() const{return _finish - _start;}size_t capacity() const{return _end_of_storage - _start;}void reserve(size_t capa){//仅支持容量扩大,不支持容量减小if (capacity() < capa){size_t sz = size();iterator tmp = new T[capa];//分清当前的是否已经有了容量,如果已经有了容量需要释放之前的容量,//如果之前没有容量仅需,将新开的空间指向我们的_start.if (_start){//这里千万不能按二进制直接拷贝.//memcpy(tmp, _start, sizeof(T) * capacity()); /*ror !!*/for (int i = 0; i < size(); i++){//=内置类型直接赋值,自定义类型使用赋值构造tmp[i]=_start[i];}delete[] _start;}//注意:此处不能直接tmp+size()来计算,因为在计算_start的时候已经已经改变了_start,//然后计算的size也并非是,准确的size。_start = tmp;_finish = tmp + sz;_end_of_storage = _start + capa;}}void resize(size_t n, T val = T()){ //1.n < size;-->删除数据if (n < size()){_finish = _start + n;}//2.n > sizeelse {//(1)如果空间不足,需要扩容+初始化if (n >= capacity()){reserve(n);}//(2)空间足够,仅需要初始化剩下的空间while (_finish != _start + n){*(_finish) = val;_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];}void push_back(const T& val){if (_finish == _end_of_storage){capacity() == 0 ? reserve(5) : reserve(capacity() * 2);}//内置类型直接赋值,自定义类型使用赋值构造*(_finish) = val;_finish++;}bool empty() const {return size() == 0;}void pop_back(){//判空assert(!empty());//我们仅需将维护尾部数据的指针向前挪一位。_finish--;}iterator erase(iterator pos){assert(pos >= _start);assert(pos < _finish);iterator end = pos ;while (end < _finish){*end = *(end + 1);end++;}_finish--;return pos;}iterator insert(iterator pos,T val){assert(pos >= _start);assert(pos < _finish);//迭代器失效问题,记录pos的相对位置int len = pos - _start;if (_finish == _end_of_storage){capacity() == 0 ? reserve(5) : reserve(capacity() * 2);}//扩容后重新计算pos,没有发生扩容pos不变pos = _start + len;iterator end = _finish;//数据挪动while (end >= pos){(*end) = *(end - 1);end--;}_finish++;(*pos) = val;return pos;}//普通迭代器iterator begin(){return _start;}iterator end(){return _finish;}//const 迭代器const_iterator begin()const {return _start;}const_iterator end()const{return _finish;}~Vector(){delete[] _start;_start = _finish = _end_of_storage = nullptr;}
private:iterator _start;//数据存储首地址iterator _finish;//有效数据尾部地址下一个地址。iterator _end_of_storage;//容量尾地址下一个地址int tmp;
};

相关文章:
C++ STL vector 模拟实现
✅<1>主页:我的代码爱吃辣 📃<2>知识讲解:C之STL 🔥<3>创作者:我的代码爱吃辣 ☂️<4>开发环境:Visual Studio 2022 💬<5>前言:上次我们已经数字会用…...
51单片机学习--红外遥控(外部中断)
需要利用下面这个红外接收头,OUT口会发出红外信号对应的高低电平,由于发送的速度很快,所以需要把OUT引脚接在外部中断引脚上,当OUT一旦产生下降沿,马上进中断,这样响应会更及时。 外部中断引脚位于P3_2和P…...
后端开发10.规格模块
概述 简介 效果图...
腾讯出了一个新聊天软件M8
众所周知,如今国内互联网,微信和QQ无疑是社交领域的霸主。 下载:https://www.123pan.com/s/BP5A-RW4xh.html 不过,它们也有各自局限性,比如难以结识新朋友、功能过于复杂等。 这让用户产生厌倦,再加上近几年AI、元宇…...
C++ QT(一)
目录 初识QtQt 是什么Qt 能做什么Qt/C与QML 如何选择Qt 版本Windows 下安装QtLinux 下安装Qt安装Qt配置Qt Creator 输入中文配置Ubuntu 中文环境配置中文输入法 Qt Creator 简单使用Qt Creator 界面组成Qt Creator 设置 第一个Qt 程序新建一个项目项目文件介绍项目文件*.pro样式…...
微信小程序时钟
微信小程序自定义时钟,模拟翻牌时钟。1、页面布局 <view class"date-time-box"><view class"date-box">{{nowDate}}</view><view class"time-box"><view><image class"pic01 {{move[0]?move…...
HttpRunner自动化工具之设置代理和请求证书验证
httprunner设置代理: httprunner 库本身没有提供设置代理的接口,但是底层使用了urllib.requests 等库,可以设置HTTP_PROXY 和HTTPS_PROXY 环境变量,常用的网络库会自动识别这些环境变量。 日常调试使用代理(如charles…...
opsForHash() 与 opsForValue 请问有什么区别?
👉:🔗官方API参考手册 如图,opsForHash()返回HashOperations<K,HK,HV>但是 opsForValue()返回ValueOperations<K,V>… 区别就是opsForHash的返回值泛型中有K,HK,HV,其中K是Redis指定的某个数据库里面某一个关键字(由…...
具有吸引子的非线性系统(MatlabSimulink实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
Linux一些常见的命令
1. 基础命令 1. ls: 列出目录内容。- 例如:ls -l 以长格式列出文件和目录。2. cd: 切换工作目录。- 例如:cd /home/user 进入 /home/user 目录。3. pwd: 显示当前工作目录的路径。4. mkdir: 创建新目录。-…...
正则表达式的基本知识
正则表达式是一种用于匹配和操作字符串的强大工具。它是由一系列字符和特殊符号组成的模式,可以用来检查字符串是否符合某种模式,进行匹配、替换、提取等操作。 下面是一些常见的正则表达式元字符和语法: 1. 字符匹配: - 普通…...
如何⽤webpack 来优化前端性能
如何⽤webpack 来优化前端性能? ⽤webpack 优化前端性能是指优化 webpack 的输出结果,让打包的最终结果在浏览器运⾏快速⾼效。 压缩代码:删除多余的代码、注释、简化代码的写法等等⽅式。可以利⽤webpack的 UglifyJsPlugin 和 ParallelUgl…...
人机交互中的混合多重反馈
人机交互中态、势、感、知的混合多重反馈是指在交互过程中综合运用不同方面的反馈信息,包括用户态度(态)、行为动势(势)、情感体验(感)和认知反馈(知)。这种多重反馈可以…...
CSS:服务器字体 与 响应式布局(用法 + 例子 + 效果)
文章目录 服务器字体定义 服务器字体使用例子 响应式布局设备类型设备特性例子 服务器字体 解决字体不一致而产生的。 首先,在网上把字体下载好。 定义 服务器字体 font-face{font-family:字体名称;src:url(字体资源路径); }使用 在需要使用的选择器里加上 font…...
24届近3年上海电力大学自动化考研院校分析
今天给大家带来的是上海电力大学控制考研分析 满满干货~还不快快点赞收藏 一、上海电力大学 学校简介 上海电力大学(Shanghai University of Electric Power),位于上海市,是中央与上海市共建、以上海市管理为主的全日…...
PostgreSQL查询慢sql原因和优化方案
PostgreSQL sql查询慢优化方案有一下几种解决方案: 1.关闭会话 查询慢sql的执行会话,关闭进程。 查看数据库后台连接进程 SELECT count(*) FROM pg_stat_activity;SELECT * FROM pg_stat_activity; 查看数据库后台连接进程,但是此条SQL不…...
Leetcode 21. 合并两个有序链表
题目描述 题目链接:https://leetcode.cn/problems/merge-two-sorted-lists/description/ 思路 两个链表都是升序链表,新建一个链表,引入伪头节点作为辅助节点,将各节点添加到伪节点之后,再用一个cur节点指向新链表的…...
[tool] Ubuntu 设置开机启动python脚本
前言: 话说我每次设置的服务器,再次开机,ip都会随之改变,固定ip有时候确定不好用。所以为啥不让让每次启动都发送ip给我呢。 步骤: sudo touch /etc/rc.local sudo chmod 777 /etc/rc.local sudo systemctl enable rc-local.se…...
「何」到底该读「なん」还是「なに」?柯桥学日语
「何」到底该读「なん」还是「なに」? 首先,讲一个规律,大家记住就行。当「何」后面所接单词的第一个发音在“た”、“だ”、“な”行时,读作“なん”。一般这种情况下,后面跟的是の、でも、です和だ。 用例ÿ…...
github - 创建组织-Team
地址记录: github创建组织(organization) - 知乎...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...
MODBUS TCP转CANopen 技术赋能高效协同作业
在现代工业自动化领域,MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步,这两种通讯协议也正在被逐步融合,形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
c# 局部函数 定义、功能与示例
C# 局部函数:定义、功能与示例 1. 定义与功能 局部函数(Local Function)是嵌套在另一个方法内部的私有方法,仅在包含它的方法内可见。 • 作用:封装仅用于当前方法的逻辑,避免污染类作用域,提升…...
Docker拉取MySQL后数据库连接失败的解决方案
在使用Docker部署MySQL时,拉取并启动容器后,有时可能会遇到数据库连接失败的问题。这种问题可能由多种原因导致,包括配置错误、网络设置问题、权限问题等。本文将分析可能的原因,并提供解决方案。 一、确认MySQL容器的运行状态 …...
k8s从入门到放弃之HPA控制器
k8s从入门到放弃之HPA控制器 Kubernetes中的Horizontal Pod Autoscaler (HPA)控制器是一种用于自动扩展部署、副本集或复制控制器中Pod数量的机制。它可以根据观察到的CPU利用率(或其他自定义指标)来调整这些对象的规模,从而帮助应用程序在负…...
土建施工员考试:建筑施工技术重点知识有哪些?
《管理实务》是土建施工员考试中侧重实操应用与管理能力的科目,核心考查施工组织、质量安全、进度成本等现场管理要点。以下是结合考试大纲与高频考点整理的重点内容,附学习方向和应试技巧: 一、施工组织与进度管理 核心目标: 规…...
