【浅尝C++】STL第二弹=>迭代器失效详解/vector常用接口使用示例/vector底层结构探索/vector模拟实现代码详解
🏠专栏介绍:浅尝C++专栏是用于记录C++语法基础、STL及内存剖析等。
🎯每日格言:每日努力一点点,技术变化看得见。
文章目录
- vector介绍
- vector常用接口及使用示例
- 构造类函数
- 迭代器的使用
- 容量操作
- 增删改查
- 迭代器失效详解与vector底层结构探索
- vector模拟实现
- 构造类函数
- 属性获取类函数
- 扩容与容量重置函数
- 插入与删除函数
- vector模拟实现代码汇总(含正向迭代器)
- vector实现二维数组
vector介绍
vector就是一个可以自动扩大容量的数组,它与顺序表具有相同的性质,如适合随机访问、尾部插入删除效率高。关于顺序表的特点,可以查阅该篇文章→数据结构线性表(含顺序表及链表)介绍
下面给出6条关于vector的概括性介绍:
- vector是表示可变大小数组的序列容器。
- 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
- 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
- vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因而存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
- 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
- 与其它动态序列容器相比(deque, list 及forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。
vector常用接口及使用示例
构造类函数
构造函数声明 | 接口说明 |
---|---|
vector() | 无参构造 |
vector(size_type n, const value_type& val = value_type()) | 构造并初始化为n个val |
vector(const vector& x) | 拷贝构造 |
vector(InputIterator first, InputIterator last) | 使用迭代器进行初始化构造 |
下面给出vector各种构造类函数的使用示例↓↓↓
#include <iostream>
#include <vector>
using namespace std;void testVector()
{//无参构造vector<int> vc1;//5个'c'构造vectorvector<char> vc2(5, 'c');for(auto ch : vc2){cout << ch << " "; }cout << endl;//使用vc2构造vc3vector<char> vc3(vc2);for(auto ch : vc3){cout << ch << " "; }cout << endl;//使用容器的起始与终止迭代器构造vectorstring s = "Jammingpro";vector<char>vc4(s.begin(), s.end());for(auto ch : vc4){cout << ch << " "; }cout << endl;
}int main()
{testVector();return 0;
}
迭代器的使用
迭代器 | 接口说明 |
---|---|
begin + end | begin获取第一个数据的位置,end获取最后一个数据的下一个位置 |
rbegin + rend | rbegin获取最后一个数据的位置,rend获取第一个数据的前一个位置 |
如上图所示,begin指向第一个元素的位置,end指向向最后一个元素的下一个位置。下面给出begin和end这俩迭代器的使用示例
#include <iostream>
#include <vector>
using namespace std;void testVector()
{vector<int> vc = {1,2,3,4,5,6,7,8};vector<int>::iterator it = vc.begin();while(it != vc.end()){cout << *it << " ";++it;}cout << endl;
}int main()
{testVector();return 0;
}
这里可以先将迭代器先理解为数组指针,it指向数组的首地址,++it就可以将指针指向下一个元素的首地址。下文将对vector的迭代器进行模拟实现。
对于反向迭代器来说,rbegin指向最后一个元素,rend指向首元素的前一个位置。其他操作与正向迭代器begin与end没有区别。对反向迭代器进行++操作,等同于对指针-1操作。也就说,反向迭代器初始化为rbegin后,每执行一次++操作,迭代器就会向前移动一个位置,而不是向后移动。
#include <iostream>
#include <vector>
using namespace std;void testVector()
{vector<int> vc = {1,2,3,4,5,6,7,8};vector<int>::reverse_iterator it = vc.rbegin();while(it != vc.rend()){cout << *it << " ";++it;}cout << endl;
}int main()
{testVector();return 0;
}
普通迭代器的类型为iterator、反向迭代器的类型为reverse_iterator,除了这两种迭代器,还有const_iterator和const_reverse_iterator,这两个迭代器用于对const类型的容器进行正反向遍历,它们不能对容器内的数据进行修改,而非const迭代器可以修改容器内的数据。↓↓↓
#include <iostream>
#include <vector>
using namespace std;void testConstVector(const vector<int> vc)
{vector<int> vc = {1,2,3,4,5,6};vector<int>::const_iterator it = vc.begin();while(it != vc.end()){*it += 1;//error!!++it;}
}void testVector()
{vector<int> vc = {1,2,3,4,5,6};vector<int>::iterator it = vc.begin();while(it != vc.end()){*it += 1;//ok!!++it;}for(auto e : vc){cout << *it << " ";}cout << endl;
}int main()
{testVector();return 0;
}
容量操作
接口声明 | 接口说明 |
---|---|
size | 获取数据个数 |
capacity | 获取容量大小 |
empty | 判断是否为空 |
resize | 改变vector的size |
reserve | 改变vector的capacity |
vector的capacity(容量)一般都会size(有效数据个数)要大。因为vector的底层是包含连续空间的顺序表,扩容时可能需要大量移动数据,扩容效率较低。因而,vector会在扩容时,按照1.5倍或2倍扩容。
下面代码用于验证vs下与g++下vector的扩容规律↓↓↓
#include <iostream>
#include <vector>
using namespace std;void testVector()
{vector<int> vc;int sz = vc.capacity();//获取初始容量for(int i = 0; i < 100; i++){vc.push_back(i);if(sz != vc.capacity()){sz = vc.capacity();cout << "vector capacity change to : " << sz << endl;}}
}int main()
{testVector();return 0;
}
vs下执行结果如下图所示,说明vs按照1.5倍左右进行扩容。
而g++下执行结果入下图所示,说明g++按照2倍左右进行扩容。
empty用于判断vector容器中的有效数据数量是否为0;reserve用于提前给vector开辟空间,如果我们预先知道将要插入1000个数据,则我们可以预先开辟1000个空间,这样可以避免频繁扩容导致的效率损失。resize:当resize指定大小n小于有效数据个数时,则会将位于下标n及n后面的有效数据删除;当resize指定大小n大于有效数据个数时,则会在size到n-1的位置填充resize传入参数中指定的数据。
下面给出这3个函数的使用示例↓↓↓
#include <iostream>
#include <vector>
using namespace std;void testVector()
{vector<int> vc;if(vc.empty()){cout << "vc is empty" << endl;}cout << "vc's capacity is " << vc.capacity() << endl;vc.reserve(100);cout << "vc's capacity is " << vc.capacity() << endl;for(int i = 0; i < 100; i++){vc.push_back(i);}cout << "vc's size is " << vc.size() << endl;
}int main()
{testVector();return 0;
}
增删改查
接口声明 | 接口说明 |
---|---|
push_back | 尾插 |
pop_back | 尾删 |
insert | 在pos前插入val |
erase | 删除pos位置的数据 |
swap | 交换两个vector的数据空间 |
operator[ ] | 想数组一样使用[ ]进行访问 |
下面给出上面各个接口的使用示例↓↓↓
#include <iostream>
#include <vector>
using namespace std;void testVector()
{vector<int> vc;vc.push_back(1);vc.push_back(2);vc.push_back(3);vc.push_back(4);vc.push_back(5);cout << "after push back:";for(auto ch : vc){cout << ch << " ";}cout << endl;vc.pop_back();cout << "after pop back:";for(auto ch : vc){cout << ch << " ";}cout << endl;vc.insert(vc.begin(), 888);cout << "after insert:";for(auto ch : vc){cout << ch << " ";}cout << endl;vc.erase(vc.begin());cout << "after erase:";for(auto ch : vc){cout << ch << " ";}cout << endl;vector<int> vc2 = {666,777,888};cout << "before swap vc2 is:" ;for(auto ch : vc2){cout << ch << " ";}cout << endl;vc2.swap(vc);cout << "after swap:";for(auto ch : vc){cout << ch << " ";}cout << endl;vc2[2] = 999;cout << "after operator[]:";for(auto ch : vc2){cout << ch << " ";}cout << endl;
}int main()
{testVector();return 0;
}
迭代器失效详解与vector底层结构探索
我们可以通过vs的监视窗口查看到,vector底层总共3个指针,_first指向堆区开辟空间的首地址,_last指向最后一个有效元素的下一位置,_end指向指向容量的下一位置。
而迭代器底层是什么呢?由下图可以看到,vector迭代器底层是一个指向堆区空间的指针。
如果我们在vector扩容器,执行vector<int>::iteartor it = vc.begin()
则可以获得vc的_first指针内容,及it的指针指向0x00000000。如果此时插入数据后,vc发生扩容,将旧空间数据拷贝到新空间,并将旧空间释放,新的_first指针内容变为0x00FCA580,而此时it中的指针仍然指向旧空间,如果此时对it进行解引用操作,就会发生非法访问的错误。(下图演示了该文字表述的内容)
vector模拟实现
我们参照SGI版本的STL命名方式,它的_start指向指向开辟的空间首地址,_final指向最后一个后效数据的下一位置,_end_of_storage指向开辟空间最后一个位置的下一位置。
下面给出即将模拟实现的vector类的框架(含默认构造及析构函数)↓↓↓
template<class T>
class vector
{
public:vector(){}~vector(){if(_start){delete[] _start;_start = _final = _end_of_storage = nullptr;}}
private:T* _start = nullptr;T* _final = nullptr;T* _end_of_storage = nullptr;
};
构造类函数
编译器自动生成的默认构造函数与拷贝赋值都是对_first、_final、_end_of_storage进行值拷贝。如下图所示。若使用vc来构造vc2,则会使得vc和vc2的成员变量保存同一份地址,当vc析构释放_first指向的空间后,vc2再析构时也会重复释放该空间,导致出错。
因此,我们需要重写拷贝构造与拷贝赋值函数
vector(const vector<T>& vc)
{_final = vc._size;_end_of_storage = vc._end_of_storage;_start = new T[_end_of_storage];memcpy(_start, vc._start, sizeof(T) * vc.size());
}vector<T>& operator=(const vector<T> vc)
{if (this != &vc){T* tmp = new T[vc._end_of_storage];memcpy(tmp, vc._start, sizeof(T) * vc._size);if (_start) delete[] _start;_start = tmp;_final = _start + vc.size();_end_of_storage = _start + vc.capacity();}
}
属性获取类函数
下面实现的empty、size、capacity、operator[]函数↓↓↓
bool empty() const
{return _start == _final;
}
size_t size() const
{return _final - _start;
}
size_t capacity() const
{return _end_of_storage - _start;
}
T& operator[](const size_t pos)
{assert(pos < size());return _start[pos];
}
扩容与容量重置函数
void reserve(const size_t& n)
{if (n > capacity()){size_t sz = size();T* tmp = new T[n];memcpy(tmp, _start, sizeof(T) * sz);if (_start) delete[] _start;_start = tmp;_final = _start + sz;_end_of_storage = _start + n;}
}void resize(const size_t& sz, const T& val = T())
{if (sz <= size()){_final = _start + sz;}else{if (sz > capacity()){reserve(sz);}for (int i = size(); i < sz; i++){_start[i] = val;}_final = _start + sz;}
}
插入与删除函数
void push_back(const T& val)
{if (size() == capacity()){int newsize = capacity() == 0 ? 10 : 2 * capacity();reserve(newsize);}_start[size()] = val;_final++;
}
void pop_back()
{assert(!empty());_final--;
}
void insert(T* pos, const T& val)
{if (size() == capacity()){int newsize = capacity() == 0 ? 10 : 2 * capacity();reserve(newsize);}T* it = _final;while (it > pos){*it = *(it - 1);--it;}*pos = val;_final++;
}
void erase(T* pos)
{assert(!empty());T* it = pos;while (it < _final){*it = *(it + 1);++it;}--_final;
}
vector模拟实现代码汇总(含正向迭代器)
★ps:insert可能会引起vector产生扩容,导致迭代器失效,SGI版本STL中将需要新的迭代器返回。下面代码中insert与erase与SGI版本返回值保持一致。
namespace jammingpro
{template<class T>class vector{typedef T* iterator;typedef const T* const_iterator;public:iterator begin(){return _start;}iterator end(){return _final;}const_iterator begin() const{return _start;}const_iterator end() const{return _final;}vector(){}vector(const vector<T>& vc){_final = vc._size;_end_of_storage = vc._end_of_storage;_start = new T[_end_of_storage];memcpy(_start, vc._start, sizeof(T) * vc.size());}vector<T>& operator=(const vector<T> vc){if (this != &vc){T* tmp = new T[vc._end_of_storage];memcpy(tmp, vc._start, sizeof(T) * vc._size);if (_start) delete[] _start;_start = tmp;_final = _start + vc.size();_end_of_storage = _start + vc.capacity();}}~vector(){if (_start){delete[] _start;_start = _final = _end_of_storage = nullptr;}}bool empty() const{return _start == _final;}size_t size() const{return _final - _start;}size_t capacity() const{return _end_of_storage - _start;}T& operator[](const size_t pos){assert(pos < size());return _start[pos];}void reserve(const size_t& n){if (n > capacity()){size_t sz = size();T* tmp = new T[n];memcpy(tmp, _start, sizeof(T) * sz);if (_start) delete[] _start;_start = tmp;_final = _start + sz;_end_of_storage = _start + n;}}void resize(const size_t& sz, const T& val = T()){if (sz <= size()){_final = _start + sz;}else{if (sz > capacity()){reserve(sz);}for (int i = size(); i < sz; i++){_start[i] = val;}_final = _start + sz;}}void push_back(const T& val){if (size() == capacity()){int newsize = capacity() == 0 ? 10 : 2 * capacity();reserve(newsize);}_start[size()] = val;_final++;}void pop_back(){assert(!empty());_final--;}iterator insert(T* pos, const T& val){int sz = pos - _start;if (size() == capacity()){int newsize = capacity() == 0 ? 10 : 2 * capacity();reserve(newsize);}T* it = _final;while (it > pos){*it = *(it - 1);--it;}*(_start + sz) = val;_final++;return _start + sz;}iterator erase(T* pos){assert(!empty());T* it = pos;while (it < _final){*it = *(it + 1);++it;}--_final;return pos;}private:T* _start = nullptr;T* _final = nullptr;T* _end_of_storage = nullptr;};
}
vector实现二维数组
如果想使用vector二维数组可以定义如下代码:
void test()
{vector<vector<int>>vc(5, vector<int>(6, 0));
}
上面的代码相当于创建了一个包含5个vector的vector容器
🎈欢迎进入浅尝C++专栏,查看更多文章。
如果上述内容有任何问题,欢迎在下方留言区指正b( ̄▽ ̄)d
相关文章:

【浅尝C++】STL第二弹=>迭代器失效详解/vector常用接口使用示例/vector底层结构探索/vector模拟实现代码详解
🏠专栏介绍:浅尝C专栏是用于记录C语法基础、STL及内存剖析等。 🎯每日格言:每日努力一点点,技术变化看得见。 文章目录 vector介绍vector常用接口及使用示例构造类函数迭代器的使用容量操作增删改查 迭代器失效详解与v…...
【pytest】pytest` 中几种常用的参数化方法
pytest 是一个强大的 Python 测试框架,它提供了多种参数化测试的方法。参数化测试允许你使用不同的输入集来运行相同的测试逻辑,从而确保代码在各种条件下都能正常工作。以下是 pytest 中几种常用的参数化方法: 1. 使用 pytest.mark.paramet…...

设计模式-装饰者模式在Java中使用实例-打印发票装饰抬头和脚注
场景 设计模式-装饰者模式在Java中的使用示例: 设计模式-装饰者模式在Java中的使用示例_java装饰者模式例子-CSDN博客 上面装饰器的调用示例如下 AbstarctComputer computer;//要买1台电脑computer new BaseComputer();//加一个内存条computer new MemoryDecor…...

parallel linux虚拟机没有root权限
前言 今天刚在parallel上装上linux虚拟机,安装的是Debian发行版。用终端输入命令时,无意间发现当前用户竟然不是root用户,岂有此理!众所周知,Linux系统一般安装之后都是默认root用户的,但是可能parallel先…...

科技下乡:数字乡村改变乡村生活方式
在科技飞速发展的时代,数字化、信息化浪潮正以前所未有的速度席卷全球。在这场科技革命中,乡村不再是滞后的代名词,而是成为了数字乡村建设的热土。科技下乡,让数字乡村成为了改变乡村生活方式的重要力量。 一、科技下乡…...
【GitLab】Ubuntu使用宝塔安装GitLab最新社区版
首先在Ubuntu安装宝塔面板 在官网可以找到脚本一键安装 安装GitLab社区版 然后在宝塔面板的“软件商店”里面找到GitLab最新社区版 12.8.1一键安装 安装过程中可能出现以下问题: 1.卡在ruby_block[wait for logrotate service socket] action run 解决办法&…...

C++入门(2)
目录 3. C输入&输出 4. 缺省(默认)参数 4.1 缺省参数概念 4.2 缺省参数分类 全缺省参数 半缺省参数 5. 函数重载 5.1 函数重载概念 6. 引用 6.1 引用概念 6.2 引用特性 6.3 常引用 6.4 使用场景 6.5 传值、传引用效率比较 6.5.1 值和引用的作为返回值类型的性能比较 6.6 引…...

Prometheus +Grafana +node_exporter可视化监控Linux + windows虚机
1、介绍 背景:需要对多台虚机进行负载可视乎监控,并进行及时的报警 2、架构图 node_exporter :主要是负责采集服务器的信息。 Prometheus :主要是负责存储、抓取、聚合、查询方面。 Grafana : 主要是…...

腾讯云容器与Serverless的融合:探索《2023技术实践精选集》中的创新实践
腾讯云容器与Serverless的融合:探索《2023技术实践精选集》中的创新实践 文章目录 腾讯云容器与Serverless的融合:探索《2023技术实践精选集》中的创新实践引言《2023腾讯云容器和函数计算技术实践精选集》整体评价特色亮点分析Serverless与Kubernetes的…...

python 字典练习
def main():dict1{姓名:张三, 工资: 5000}dict2{姓名:李四, 工资: 6600}dict3{姓名:王五, 工资: 8500}dict4{}dict1.update(dict2)#字典的写法print(dict1)dict1.setdefault("3月",0)#存在不作为,不存在则增补print(dict1)names[]list[dict1,dict2,dict3]…...

Postman进阶功能实战演练
Postman除了前面介绍的一些功能,还有其他一些小功能在日常接口测试或许用得上。今天,我们就来盘点一下,如下所示: 1.数据驱动 想要批量执行接口用例,我们一般会将对应的接口用例放在同一个Collection中,然…...
Flink基于Hudi维表Join缺陷解析及解决方案
Hudi,这个近年来备受瞩目的数据存储解决方案,无疑是大数据领域的一颗耀眼新星。其凭借出色的性能和稳定性,以及对于数据湖场景的深度适配,赢得了众多企业和开发者的青睐。然而,正如任何一项新兴技术,Hudi在…...

3.31学习总结
(本次学习总结,总结了目前学习java遇到的一些关键字和零碎知识点) 一.static关键字 static可以用来修饰类的成员方法、类的成员变量、类中的内部类(以及用static修饰的内部类中的变量、方法、内部类),另外可以编写static代码块来优化程序性…...

Android Studio控制台输出中文乱码问题
控制台乱码现象 安卓在调试阶段,需要查看app运行时的输出信息、出错提示信息。 乱码,会极大的阻碍开发者前进的信心,不能及时的根据提示信息定位问题,因此我们需要查看没有乱码的打印信息。 解决步骤: step1: 找到st…...

itextPdf生成pdf简单示例
文章环境 jdk1.8,springboot2.6.13 POM依赖 <dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.13</version></dependency><dependency><groupId>com.ite…...

【Linux系列】tree和find命令
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

AI预测福彩3D第23弹【2024年4月1日预测--第4套算法重新开始计算第9次测试】
今天继续对第4套算法进行测试,因为第4套算法已连续多期命中,相对来说还算稳定。好了,废话不多说了,直接上预测的结果吧~ 2024年4月1日福彩3D的七码预测结果如下 第一套: 百位:0 1 …...

Java常见限流用法介绍和实现
目录 一、现象 二、工具 1、AtomicInteger,AtomicLong 原子类操作 2、RedisLua 3、Google Guava的RateLimiter 1) 使用 2) Demo 3) 优化demo 4、阿里开源的Sentinel 三、算法 1、计数限流 &…...
算法——图论:判断二分图(染色问题)
题目:. - 力扣(LeetCode) 方法一:并查集 class Solution { public:vector<int>father;int find(int x){if (father[x] ! x)father[x] find(father[x]);return father[x];}void add(int x1, int x2){int fa1 find(x1), f…...

三步提升IEDA下载速度——修改IDEA中镜像地址
找到IDEA的本地安装地址 D:\tool\IntelliJ IDEA 2022.2.4\plugins\maven\lib\maven3\conf 搜索阿里云maven仓库 复制https://developer.aliyun.com/mvn/guide中红框部分代码 这里也是一样的: <mirror><id>aliyunmaven</id><mirrorOf>*&…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

TDengine 快速体验(Docker 镜像方式)
简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能,本节首先介绍如何通过 Docker 快速体验 TDengine,然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker,请使用 安装包的方式快…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...

如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
SQL Server 触发器调用存储过程实现发送 HTTP 请求
文章目录 需求分析解决第 1 步:前置条件,启用 OLE 自动化方式 1:使用 SQL 实现启用 OLE 自动化方式 2:Sql Server 2005启动OLE自动化方式 3:Sql Server 2008启动OLE自动化第 2 步:创建存储过程第 3 步:创建触发器扩展 - 如何调试?第 1 步:登录 SQL Server 2008第 2 步…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态
前言 在人工智能技术飞速发展的今天,深度学习与大模型技术已成为推动行业变革的核心驱动力,而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心,系统性地呈现了两部深度技术著作的精华:…...