【STL源码剖析】vector类模拟实现 了解底层-走进底层-掌握底层【超详细的注释和解释】

news/2024/4/29 7:56:50/文章来源:https://blog.csdn.net/Yu_Cblog/article/details/126930337

今天博主继续带来STL源码剖析专栏的第二篇博客了!
今天带来vector的模拟实现!
其实在很多人学习C++过程中,都是只学习一些STL的使用方式,并不了解底层的实现。博主本人认为,这样的学习这样的技术是不深的。如果我们想要熟悉的掌握一门语言,我认为,底层的实现必不能少!
但是,想从0开始模拟实现STL的容器,需要我们熟悉C++的语法,特别是类和对象部分的知识!
博主学习C++到现在,我认为C++类和对象基本语法的学习比任何部分都要重要,而我花在这上面的时间也是最多的!只要搞清楚C++面向对象编程的基本规则,我们才能在STL的世界里游刃有余!
所以我希望大家在学习STL之前,先将数据结构与算法,C++的类和对象部分内容掌握熟悉!


前言

那么这里博主先安利一下一些干货满满的专栏啦!

手撕数据结构https://blog.csdn.net/yu_cblog/category_11490888.html?spm=1001.2014.3001.5482这里包含了博主很多的数据结构学习上的总结,每一篇都是超级用心编写的,有兴趣的伙伴们都支持一下吧!
算法专栏https://blog.csdn.net/yu_cblog/category_11464817.html这里是STL源码剖析专栏,这个专栏将会持续更新STL各种容器的模拟实现。

STL源码剖析icon-default.png?t=M85Bhttps://blog.csdn.net/yu_cblog/category_11983210.html?spm=1001.2014.3001.5482


实现过程中要注意的一些点

vector类其实是我们非常常用的一个容器,其底层其实就是一个顺序表。

博主在带大家实现的过程中,并不会把STL里面每一个成员函数都去模拟实现一次,我们模拟实现容器底层的目的其实是去学习里面实现的思路。

因此,博主只会将一些重要、常用的成员函数模拟实现给大家。

另外,除了博主实现的源代码之外,博主还提供一份测试代码,它可以帮助我们检查自己写的代码是否准确。

在测试代码中遇到的问题和解决思路以及一些细节的处理,博主都在.h和.cpp代码里用注释的形式给大家说明白(注释满满干货不要错过噢)。

希望大家可以从中学到知识。

MyVector.h完整源代码

#pragma once
#include<cassert>
#include<string.h>
#include<algorithm>
#include<functional>
namespace yufc {template<class T>class vector {public:typedef T* iterator;//这个要放成共有,不然迭代器外面访问不了 -- 定义迭代器类型是指针类型typedef const T* const_iterator;//vector的迭代器就是原生指针iterator begin() {return _start;}iterator end() {return _finish;}//如果不提供const迭代器 -- 如果传了const的vector是遍历不了的,因为权限不能放大const_iterator begin()const { //不能返回普通迭代器,要返回const迭代器return _start;}const_iterator end()const {return _finish;}vector() :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr) {}~vector() {delete[] _start;_start = _finish = _end_of_storage = nullptr;}//stl的vector还支持使用迭代器区间去构造//为什么需要一个其它类型的迭代器?//因为他要支持用其它容器的迭代器的区间构造template<class InputIterator>vector(InputIterator first, InputIterator last) :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr){while (first != last) {//不能直接push_back(),直接push_back()会崩溃 -- 因为没有初始化 -- 野指针push_back(*first);++first;}}vector(size_t n, const T& val = T()) :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr){//用n个值去构造reserve(n);for (size_t i = 0; i < n; i++) {push_back(val);}}//传统拷贝构造
#if 0vector(const vector<T>& v) {_start = new T[v.size()];//这里是给size还是capacity呢?STL没有要求//memcpy(_start, v._start, sizeof(T) * v.size());//解决vector<vector<int>>深拷贝的情况,memcpy不能帮助内层的自定义类型完成深拷贝,赋值可以(我们写好了自定义类型的赋值)for (size_t i = 0; i < v.size(); i++) {_start[i] = v._start[i];}_finish = _start + v.size();_end_of_storage = _start + v.size();//因为T[]里面给了size(),所以这里也应该是v.size()}
#endif//写法2
#if 0vector(const vector<T>& v) :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr){reserve(v.size());for (const auto& e : v) { //注意:这里拷贝过去一定要引用,不然又要拷贝了//写深拷贝自己还要调深拷贝 -- 肯定是会出问题的push_back(e);}//不需要去调整_finish,因为push_back()已经搞定了}
#endif//现代写法拷贝构造 -- 复用迭代器区间构造函数void swap(vector<T>& v) {std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);}
#if 1vector(const vector<T>& v) :_start(nullptr), _finish(nullptr), _end_of_storage(nullptr){vector<T>tmp(v.begin(), v.end());swap(tmp);//当然自定义类型自己写的swap更高效 -- 我们自己实现一个}
#endifvector<T>& operator=(vector<T> v) { //v1=v2//v是v2的拷贝,而且是局部的,swap完之后给到v1,v还会自动析构(因为是局部对象)swap(v);return *this;}size_t capacity()const {return _end_of_storage - _start;}size_t size()const {return _finish - _start;}void reserve(size_t n) {if (n > capacity()) {T* tmp = new T[n];size_t sz = size();if (_start) {//memcpy(tmp, _start, sizeof(T) * sz);//拷贝size()个字节过去,先放到tmp里面//同样,解决//vector<自定义类型>的问题for (size_t i = 0; i < sz; i++) {tmp[i] = _start[i];//当T是自定义类型时,调用T类型的operator=}delete[] _start;}_start = tmp;//_finish = _start + size();//这里不要现场去算size()//因为size()是用start减出来的,上面那一行start已经变了//所以在前面我们最好保留一下size先_finish = _start + sz;_end_of_storage = _start + n;}}void resize(size_t n, const T& val = T()) {//1.扩容+初始化//2.初始化//3.删除数据if (n > capacity()) {reserve(n);}if (n > size()) {//初始化填值while (_finish < _start+n) {*_finish = val;++_finish;}}else {_finish = _start + n;}}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& x) {//加了const保证传什么类型都行,因为隐式类型转换临时变量具有常性if (_finish == _end_of_storage) {reserve(capacity() == 0 ? 4 : capacity() * 2);}*_finish = x;++_finish;}void pop_back() {assert(_finish > _start);//为空是不能删的--_finish;}void insert(iterator pos, const T& x) {assert(pos >= _start);assert(pos <= _finish);if (_finish == _end_of_storage) {//扩容//记住pos和start的相对位置size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;//更新pos的位置 -- 解决迭代器失效}//挪动数据iterator end = _finish - 1;while (end >= pos) { // insert要扩容的时候,这个循环就失效了,停不下来了,为什么?*(end + 1) = *end;end--;}//扩容之后,旧空间的数据拷贝到新空间//旧空间已经被释放了 -- pos是指向旧空间的一个数字的位置的//pos成了野指针! -- 迭代器失效//所以我们要把pos更新一下// -- 修改了内部的pos之后,其实问题还没有根本的解决*pos = x;++_finish;}iterator erase(iterator pos) {assert(pos >= _start);assert(pos < _finish);//挪动覆盖删除iterator begin = pos + 1;while (begin < _finish) {*(begin - 1) = *begin;++begin;}--_finish;//删除了位置的下一个位置 -- 还是posreturn pos;}T& front() {assert(size() > 0);return *_start;}T& back() {assert(size() > 0);return *(_finish - 1);}private:iterator _start;//start相当于整个数组的开始位置iterator _finish;//[_start,_finish)iterator _end_of_storage;};
}

 test.cpp测试源文件完整代码

#define _CRT_SECURE_NO_WARNINGS 1#include"MyVector.h"
#include<iostream>
#include<vector>
using namespace std;void PrintVector(yufc::vector<int>& v) {//在没有写拷贝构造的时候,这里一定要引用的,不然浅拷贝,释放两次会出问题yufc::vector<int>::iterator it = v.begin();while (it != v.end()) {cout << *it << " ";it++;}cout << endl;
}
void PrintVector(const yufc::vector<int>& v) {yufc::vector<int>::const_iterator it = v.begin();while (it != v.end()) {cout << *it << " ";it++;}cout << endl;
}
void test_vector1() {yufc::vector<int>v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.push_back(6);for (int i = 0; i < v.size(); i++) {++v[i];cout << v[i] << " ";}cout << endl;
}
void test_vector2() {yufc::vector<int>v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.push_back(6);yufc::vector<int>::iterator it = v.begin();while (it != v.end()) {(*it)--;cout << *it << " ";it++;}cout << endl;for (auto& e : v) {e++;cout << e << " ";}cout << endl;it = v.begin();while (it != v.end()) {cout << *it << " ";it++;}cout << endl;PrintVector(v);
}
void test_vector3() {const yufc::vector<int>v;
#if 0 //改不了的v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.push_back(6);
#endifPrintVector(v);//范围for虽然是傻瓜式的替换 -- 但是它也是会调用const迭代器的
}
//迭代器失效问题
void test_vector4() {yufc::vector<int>v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);//v.push_back(5); // 我们发现  -- 原来4个数据,insert之后要扩容的时候,就出现问题了!//v.push_back(6);yufc::vector<int>::iterator it = v.begin();while (it != v.end()) {cout << *it << " ";it++;}cout << endl;//auto p = find(v.begin(), v.end(), 3);if (p != v.end()) {v.insert(p, 30);//在内部初步解决迭代器失效之后,其实问题还是没有根本解决!//因为内部的pos更新不会影响p//所以我们在使用的时候//在p位置插入数据以后,不要访问p,因为p可能失效了//因为我们使用STL的时候,不了解底层的扩容机制,所以以后我们insert之后,不要去访问p位置! -- 可能会有问题//那能否把insert第一个参数改成&行吗? -- 尽量不要这样做 -- 和库保持一致好//虽然我们看似解决了问题 -- 但是改了可能会引出其它问题 -- 比如,头插,我们想传v.begin();v.insert(v.begin(), 0);//编不过,因为类型不匹配
#if 0cout << *p << endl;v.insert(p, 40);
#endif}PrintVector(v);
}
//erase迭代器会失效吗?
//库里面的erase会失效吗? -- 不知道
//STL并没有规定什么机制
//有没有一种可能,当size()<capacity()/2的时候,缩一下容(缩容:以时间换空间)
//反正用完那个别访问,别动那个p就行了!
void test_vector5() {yufc::vector<int>v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);auto p = find(v.begin(), v.end(), 3);if (p != v.end()) {v.erase(p);}PrintVector(v);
}
void test_vector6() {yufc::vector<int>v;
#if 0v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);
#endif//情况1和2//我们发现此时有这个5 -- 程序正常,没有 -- 崩溃
#if 1//情况3v.push_back(1);v.push_back(2);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);
#endif//要求删除所有的偶数auto it = v.begin();//情况3的时候,it会跳过end(),直接继续往后了 -- 崩溃//所以下面这段算法是不正确的!//其实我们看源代码可以发现 -- erase是有返回值的 -- 会更新一下迭代器//STL规定erase返回删除位置下一个位置的迭代器!//vs这个编译器是查的很严格的 -- erase之后不允许去访问! -- 访问就报错//Linux下就不同while (it != v.end()) {if (*it % 2 == 0) {it = v.erase(it);//我们要返回一个it才行}else {++it;}}PrintVector(v);
}
//总结
//其实迭代器失效,我们在自己做OJ的时候也能体会的,插入或者删除之后,迭代器指向了它不该指向的地方
//在使用vector的迭代器的时候,要多调试!void test_vector7() {//系统默认的拷贝//1.自定义类型 -- 调拷贝构造 -- 默认生成的 -- 浅拷贝//2.内置类型 -- 值拷贝 -- 浅拷贝yufc::vector<int>arr;arr.push_back(1);arr.push_back(2);arr.push_back(3);for (auto i : arr) {cout << i << " ";}cout << endl;yufc::vector<int>arr2 = arr;//调用系统默认的话就是浅拷贝,这样程序肯定会崩溃的 -- 析构了两次//而且如果是浅拷贝,改一边的值另一边也会被改变arr2[0] = 100; //两边都会被改变的 -- 所以我们需要深拷贝!for (auto i : arr) {cout << i << " ";}cout << endl;for (auto i : arr2) {cout << i << " ";}
}
void test_vector8() {string s("hell");yufc::vector<int>vs(s.begin(), s.end());for (auto i : vs) {cout << i << " ";}cout << endl;//赋值yufc::vector<int>v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);vs = v;//要实现一个深拷贝//当然,如果f11 -- 这里是先进去拷贝的,因为传参要拷贝//走完拷贝就进去赋值了for (auto i : vs) {cout << i << " ";}
}
//其实内置类型也有构造
void test_type() {int i = 0;int j = int();int k = int(10);
}
void test_vector9() {//vector<int>v = { 1,2,3,4,5 };vector<int>v(10);yufc::vector<int>v(10, 1);//这样就报错了,如果传了两个参数都是int//这里出问题就是匹配问题,它匹配到不该去的地方去了//为什么 -- 因为迭代器区间构造那个函数,更适合这样传参 -- 所以进到那里去了//解决://1.传参的时候写(10u,1)表示这个是个usigned int类型//2.把vector(size_t n,const T&val=T())里面的size_t改成int,也可以解决 -- 但是stl官方给的是size_t//3.复制一份,改成int,弄个重载就行 -- stl_3.0是这样解决的for (auto i : v) {cout << i << " ";}cout << endl;
}
void test_vector10() {yufc::vector<int>v;v.resize(10, 1);for (auto e : v) {cout << e << " ";}cout << endl;yufc::vector<int>v1;v1.reserve(10);v1.push_back(1);v1.push_back(2);v1.push_back(3);v1.push_back(4);v1.resize(8, 8);for (auto e : v1) {cout << e << " ";}cout << endl;v1.resize(20, 20);for (auto e : v1) {cout << e << " ";}cout << endl;v1.resize(3);for (auto e : v1) {cout << e << " ";}cout << endl;
}//vector到目前为止还有最后一个坑还没有解决
//我们用自己的vector测试一下杨辉三角
class Solution {
public:yufc::vector<yufc::vector<int>> generate(int numRows) {yufc::vector<yufc::vector<int>> ret(numRows);for (int i = 0; i < numRows; ++i) {ret[i].resize(i + 1);ret[i][0] = ret[i][i] = 1;for (int j = 1; j < i; ++j) {ret[i][j] = ret[i - 1][j] + ret[i - 1][j - 1];}}return ret;}
};
void test_vector11() {Solution().generate(10);//报错 -- 在函数结束的时候崩溃了//修改一下 -- 把函数改成void类型,不return -- 没事了//为什么?//拷贝有问题了 -- 普通的vector深拷贝没问题 -- 这里的深拷贝有问题//里面是浅拷贝,外面是深拷贝// //            //即:我们的vector是深拷贝了 -- 但是里面的自定义类型没有深拷贝!!!!!!!!!!//我们拷贝的时候,外面的空间是深拷贝,但是里面的东西,还是指向以前的地方//传统写法是因为memcpy导致的 -- 我们将memcpy改成一个一个赋值就行了(这样的话自定义类型也可以完成深拷贝(调用自己的deepcopy))//现代写法是因为因为要调用push_back(),而push_back()里面调用reserve(),是reserve()里面的memcpy出问题
}
int main() {//test_vector1();//test_vector2();//test_vector3();//test_vector4();//test_vector5();//test_vector6();//test_vector7();//test_vector8();//test_type();//test_vector9();//test_vector10();test_vector11();return 0;
}//我们一定要清晰一个点:
//STL只是一个规范
//这个规范怎么去实现,是没有规定的!
//VS-PJ版   g++ SGI版

尾声

看到这里,相信大家对vector类的模拟实现已经有一定的了解了!vector的模拟实现,是我们掌握STL的开始,后面,博主将会给大家带来list,stack等等STL容器的模拟实现,持续关注,订阅专栏,点赞收藏都是我创作的最大动力。

转载时请注明作者和出处。未经许可,请勿用于商业用途 )
更多文章请访问我的主页

@背包https://blog.csdn.net/Yu_Cblog?spm=1000.2115.3001.5343

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

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

相关文章

超省钱的线上打印方法我找到了

有不少网友都发现这样一个现象,这就是当自己需要打印一些纸质资料的时候,例如孩子的学习资料、比较重要的一些办公资料等,在路边很难找到一家打印店。有时候好不容易找到一家打印店,却发现打印一张A4的纸质资料,需要一块钱。如果一份纸质资料一共有30页,打印30张A4格式的…

Delphi WebBrowser完成网页自动登录

订制一些WEB数据抓取项目时,会遇到需要先登录网站系统,这时候我们通常会做成由系统自动登录的方式。 分析网页 要完成这项功能,首先需要对项目的网页进行分析,找到关键数据项的ID。如下图中的。1、用户名的ID名称;2、密码对应的ID名称;3、登录按钮对应的ID名称。 完成自动…

Springboot毕设项目公职备考在线学习平台e1h19(java+VUE+Mybatis+Maven+Mysql)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis Maven Vue 等等组成&#xff0c;B/…

递归回溯实战+思想

目录 排列&#xff08;提供元素无重复&#xff0c;并且不可以重复选择&#xff09; 排列&#xff08;提供的元素重复了&#xff0c;但是同个位置的元素不能复选&#xff09; 组合&#xff08;提供的元素没有重复&#xff0c;并且可以重复选择相同位置元素&#xff09; 子集…

进程关系~

进程关系一、进程组二、会话三、控制终端四、函数tcgetpgrp、tcsetpgrp和tcgetsid五、作业控制五、孤儿进程组一、进程组 每个进程除了有一进程ID之外&#xff0c;还属于一个进程组&#xff0c;进程组是一个或多个进程的集合。同一进程组中的各进程接收来自同一终端的各 种信号…

Eclipse2022创建SSM项目及问题解决

Eclipse2022创建SSM项目及问题解决 使用Eclipse创建SSM项目的过程中会遇到一些问题&#xff0c;相对于IDEA而言更为繁琐&#xff0c;该篇文章是在使用Eclipse2022&#xff0c;并且设备上已经安装、配置好了Tomcat和Maven的基础之上进行的&#xff0c;目的是为了记录在Eclipse上…

Windows部署JMeter的压力测试

1.安装Windows版本Java 直接下载Java exe格式程序包 官网下载 点击 2.下载JMeter的压缩包 官网下载地址请 点击 或者复制这个URL: https://dlcdn.apache.org//jmeter/binaries/apache-jmeter-5.5.tgz 在浏览器上&#xff0c;会自动下载 下载下来解压即可。 3.启动JMeter 进入…

Linux内核设计与实现 第十二章 内存管理

因为内核内存需要节省着用&#xff0c;内核处理内存分配错误比较麻烦等&#xff0c;所以内核中获取内存不用户空间获取内存复杂得多。 本章讨论内核是如何管理内存和内核之中获取内存的办法。 12.1页 a) 可以通过 getconf 命令来查看系统的page的大小&#xff1a; [wangyubi…

Linux开发_CentOS7.4服务器安装NFS、NGINX服务器,ffmpeg、Qt环境

1. 环境介绍 环境介绍&#xff1a;采用的是华为云的ECS弹性云服务器–镜像安装的CentOS7.4 64位 -----是服务器版&#xff0c;非桌面版哦。 在CentOS7.4服务器版本的环境下搭建NFS服务器、安装ffmpeg、安装nginx服务器、部署Qt编译环境。 &#xff08;1&#xff09;配置NGIN…

河北稳控科技几种振弦采集仪的主要区别是什么?

河北稳控科技几种振弦采集仪的主要区别是什么?VH系列属于手持系列,多用于振弦传感器现场单次测量使用;VH501TC采集读数仪,设备是专用的多类型传感器手持式读数仪,主测传感类型为单弦式振弦传感器,辅测传感类型为电压、电流传感。采用 32 位 ARM 处理器和大尺寸全彩屏、阵…

无人机群编队分析的定位问题 分析与思考-1(数学建模竞赛2022年B题)

2022年高教社杯全国大学生数学建模竞赛结束了&#xff0c;在此我们对 2022年 B题 进行一些分析与思考。 1. 初步印象 2022年 B题 &#xff08;无人机遂行编队飞行中的纯方位无源定位&#xff09;是一个有趣的题目。 随着无人机技术的快速发展&#xff0c;早已从高科技变做寻常…

【Java】运算符

我不去想是否能够成功 既然选择了远方 便只顾风雨兼程 —— 汪国真 目录 1. 认识运算符 1.1 认识运算符 1.2 运算符的分类 2. 算术运算符 2.1 四则运算符 2.2 复合赋值运算符 2.3 自增 / 自减 运算符 3.关系运算符 4.逻辑运算符 4.1 逻辑与 && 4.2 逻…

分库分表实践

分库分表实践 分库分表概念以及使用场景 分库分表用来解决单表数据量太大&#xff0c;引起的性能问题。使用分库分表后能够根据特定路由键值将数据分布在不同库以及不同表中&#xff0c;解决了单表数据量的性能、运维等问题。一般来讲&#xff0c;单一数据库实例的数据的阈值…

【网络】HTTP协议详解

&#x1f600;大家好&#xff0c;我是白晨&#xff0c;一个不是很能熬夜&#x1f62b;&#xff0c;但是也想日更的人✈。如果喜欢这篇文章&#xff0c;点个赞&#x1f44d;&#xff0c;关注一下&#x1f440;白晨吧&#xff01;你的支持就是我最大的动力&#xff01;&#x1f4…

svn 代码迁入gitlab

window中安装好git客户端,右键空白处,点选git bash here进入git界面,输入命令 将svn38163之后的所有记录都备份那:git svn clone -r 38163:HEAD svn地址 --no-metadata trunk(本地电脑目录名) --username *** 备份所有提交记录:git svn clone svn地址 --no-metadata …

Linux安装Python 以及过程中的命令详细介绍

下载源码包 打开 Python 官网 找到需要的安装包 获取了资源的链接后&#xff0c;进入Linux下载&#xff0c;wget意思是webget&#xff0c; 即下载 wget https://www.python.org/ftp/python/3.10.7/Python-3.10.7.tgz目录下会新增 这样源码包就下载好了。 如果下载太慢&…

二叉树与递归问题

目录 一&#xff1a;求二叉树的深度 二&#xff1a;二叉树反转 三&#xff1a;二叉树镜像判断 四&#xff1a;递归的终止条件 用递归解决的问题必须注意的&#xff1a; 递归的终止条件&#xff0c;也就是递归的出口&#xff08;否则&#xff1a;栈溢出&#xff09;递归的过…

决策树简介

决策树简介 决策树实际上是一个布尔函数,它的输出可以是“0 或 1”或“-1 或 +1”或“-1、0 或 +1”。决策树的大小等于其中存在的节点数,其深度等于从顶部到根的最长路径的长度。 错误率:训练集始终是给模型的标记示例,模型训练得越多,其错误率就越低。 训练样本 = { set…

程序里对象很深很大,可以用这个设计模式缓解一下

如果一个类的有非常多的属性&#xff0c;层级还很深。这个妥妥的是我的对象很大&#xff0c;你创建的时候忍一下......那你每次要创建的时候都忍一下&#xff1f;有没有一种好的方式让我们创建太的时候使用体验更好一点呢? 今天的文章里就给大家介绍一种设计模式&#xff0c;来…

C++多线程的线程返回值问题

对于多线程可执行对象的返回值是何时返回&#xff0c;以及得到的呢&#xff1f; 对于需要用到线程返回值的线程要使用future类对象来实现 文章目录future对象async()launch::deferred参数launch::async参数packaged_taskpromisefuture对象 是一个类模板 提供访问异步对象的操作…