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

wordpress 网站静态页面wordpress浮窗插件

wordpress 网站静态页面,wordpress浮窗插件,如何外贸网站推广,设计方案格式模板范文鉴于读者的响应#xff0c;打算将文章拆分一下#xff0c;方便观看#xff0c;基本接口可看 深入浅出STL之vector类 一、源码引入 以下我所介绍的都是基于【SGI】版本的STL#xff0c;对源码有兴趣的同学可以去看看 侯捷老师的《STL源码剖析》 然后呢我们就去调出【vector… 鉴于读者的响应打算将文章拆分一下方便观看基本接口可看 深入浅出STL之vector类 一、源码引入 以下我所介绍的都是基于【SGI】版本的STL对源码有兴趣的同学可以去看看 侯捷老师的《STL源码剖析》 然后呢我们就去调出【vector】的一些核心源码这里我们主要关注的就是这个使用原生指针value_type*所定义出来的迭代器 iterator然后我们又看到了保护成员[start]、[finish]、[end_of_stroage]。看到它们你是否有想起我们在 模拟string 的时候写到过的 [a]、[size]、[capacity]没错它们就存在着一定的对应关系 但是呢只看上面这些成员变量还不够我们要将其带入到具体的场景中例如下面有两个接口分别为【尾插】和【扩容】对于push_back()封装得没有那么厉害读者结合下面的图应该就能看得懂分别就是 未满追加的逻辑和已满扩容的逻辑那对于reserve()来说就是一个扩容的逻辑【allocate_and_copy】是开辟和拷贝空间那【deallocate】就是释放空间。在扩完容之后不要忘了去对三个成员变量做更新这一块的模拟实现我在下面马上就会讲到 最后的话我们再来看看 构造函数construct和析构函数destroy光看代码不知你是否回忆起了我们曾经在 C/C内存管理 中有讲到【定位new】这个概念而且提到了 内存池 这个概念其实我们在调用构造函数的时候都是通过【空间适配器】在 内存池 中开出空间那在出了作用域之后这些所开的空间都要销毁了所以就会去调用析构函数完成释放 对于上面的这些源码呢读者可以在学习了STL一段时间后配合侯捷老师的《STL源码剖析》再去展开阅读因为考虑到读者的基础就不在继续深入讲解了~ 二、模拟实现 然后我们就来模拟实现一下【vector】中的各种接口 还是一下我们先简述一下整体的架构。这个vector类还是包在【bit】这个命名空间中而对于这个类而言我要将其定义为一个 模版类这一块如果还有同学不太熟悉的话可以去看看 C模版其他部分可以看到迭代器我定义的就是原生指针类型然后将[_start]、[_finish]、[_end_of_storage]也定义为了三个迭代器类型并且采用提前声明的形式将它们都初始化为nullptr这样当我们后面在写 构造函数和析构函数 的时候就不需要再去做初始化了 namespace bit {templateclass Tclass vector {public:typedef T* iterator;typedef const T* const_iterator;// 主要接口函数private:iterator _start nullptr;iterator _finish nullptr;iterator _end_of_storage nullptr;}; }1、迭代器 首先的话简单一点来实现一下迭代器分为非const版本和const版本 iterator begin() {return _start; }iterator end() {return _finish; }const_iterator begin() const {return _start; }const_iterator end() const {return _finish; }2、容量 然后我们来讲讲容量相关的接口首先的话就是【size】和【capacity】这两个接口 size_t size() {return _finish - _start; }size_t capacity() {return _end_of_storage - _start; }对于【size】而言指的是当前这个容器中的数据个数那也就是我们在上面所讲的_start和_finish这两个迭代器之间的距离我们之前有说到过迭代器它的底层其实就是指针那要计算出两个指针之间的数据个数的话让它们做一个相减_finish - _start那对于整个容器的容量【capacity】来说即为_end_of_storage - _start。读者通过下图便可一目了然地看出来 然后呢再来说说扩容这一块的接口【reserve】首先在一进来的时候我们要去做一个判断只有当所传入的值要比原先的capacity()来得大的时候我们才去执行一个扩容的逻辑在内部的扩容逻辑中可以看到我们使用到了前面所定义的模版参数T这样去写的话就可以根据不同的类型参数开出不同的空间接下去我们所执行的就是拷贝的逻辑采取到的是内存函数memcpy()拷贝完后再去释放原空间接下去把这些成员变量去做一个更新即可 看着逻辑很清晰但是呢下面的代码存在着非常多的漏洞 void reserve(size_t n) {if (n capacity()){T* tmp new T[n]; // 开一块新空间if (_start){memcpy(tmp, _start, sizeof(T) * size());delete[] _start;}_start tmp;_finish _start size();_end_of_storage _start n;} }我们这里再写一个push_back的接口后面讲让代码先跑起来 void push_back(const T x) {if (_finish _end_of_storage){size_t newCapacity capacity() 0 ? 4 : capacity() * 2;reserve(newCapacity);}*_finish x;_finish; }第一轮测试 — 空指针异常 下面是测试的代码 void test_vector1() {bit::vectorint v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);for (auto e : v){cout e ;}cout endl; }但是呢在运行起来后却发现程序出现了崩溃这是为什么呢 按下【F5】以调试的方式运行起来就可以发现有地方出现了 空指针异常 进一步我们通过【调试窗口】再来看看很明显得就看到这个_finish的值为【0x00000000】 知道了问题所在接下去我们就通过调试一步步地来看。虽然这个代码是崩在191但是呢其实真正的问题还是出在【reserve】这个扩容的逻辑中随着我们一步一步地去看可以看到_start和_end_of_storage这两个都没什么问题但是_finish就是没有什么变化所以呢我们可以锁定到下面这句话 _finish _start size();此时就需要去看看这个【size】了之前我们使用的是_finish - _start来计算的 size()在执行这句话时_start已经发生了改变因为我们去开出了一块新的空间但是这时_finish的值还是一开始的【nullptr】那么这个 size() 最后计算出来的大小即为 -_start此时再和_start去做一个结合的话即为 0 所以上述就是为什么这个_finish的值为【0x00000000】原因那我们要如何去修改呢 首先第一种解决方案就是先去更新这个_finish用开出空间的 tmp 去做一个更新然后再用 tmp 去更新_start这样就不会出现问题了 _finish tmp size(); _start tmp; _end_of_storage _start n;通过调试来观察看看就发现确实不为空了 但是呢上面这种方案的话可能你的徒弟在维护你的代码的时候就会觉得很奇怪又给改回去了导致原先的问题再度发生所以我们可以采取下面这种策略 我们可以在每次没开始扩容之前我们都可以去事先保存一下这个 size()后面的更新顺序就不需要发生变动了在加的时候加上sz即可 if (n capacity()) {// 先保存一下原先的size()size_t sz size();T* tmp new T[n]; // 开一块新空间if (_start){memcpy(tmp, _start, sizeof(T) * size());delete[] _start;}_start tmp;_finish _start sz;_end_of_storage _start n; }通过调试再来看到确实也可以起到同样的效果 但是呢这还没完【reserve】接口还是存在问题 第二轮测试 — memcpy的拷贝问题 下面是我们要进行第二轮测试的代码内部类型使用的是 string类 void test_vector2() {bit::vectorstring v;v.push_back(11111);v.push_back(22222);v.push_back(33333);v.push_back(44444);for (auto e : v){cout e ;}cout endl; }运行起来看并没有什么问题 但是呢当我再去push_back(55555)的时候程序却出现了问题 那此时有的同学脑子转得很快感觉到一定是【reserve】扩容的地方出现了问题 于是经过我们的排查先定位到了这一句有同学就觉得是不是因为每次sizeof(T)的对象大小不一样了? memcpy(tmp, _start, sizeof(T) * size());我觉得上述这个老铁提出来的问题非常好我们一起来看看。请读者思考一下下面的结果是多少 void test_vector3() {string s1(11111);string s2;string s3(222222222222222222);cout sizeof(s1) endl;cout sizeof(s2) endl;cout sizeof(s3) endl; }如果有阅读过 深入浅出STL之string类 的同学一定可以知道在VS下对于每个string对象的大小都是固定死的均为 28B即使是通过不同的构造形式构造出来的对象也是一样的 接下去呢就带读者好好地通过调试观察一下 v.push_back(1111111111111111); v.push_back(2222222222222222); v.push_back(3333333333333333); v.push_back(4444444444444444); v.push_back(5555555555555555);如果对深浅拷贝这一块比较了解的同学一定可以知晓这里很明显地发生了一个 浅拷贝 的问题所以导致在delete[] _start的时候发生了一个 并发修改 的问题 这就导致了我们在释放原本的这块空间时导致拷贝后的这块空间也造成了另一块空间的问题 可能有的读者还是不太理解这其中的原理我们通过画图再来看看 可以看到在扩容的时候我们去开出了一块新的空间然后使用memcpy()将数据原封不动地拷贝到了另一块空间中再去做了一个扩容那在上面我们也看到过了就是因为这个memcpy()原封不动拷贝的问题就使得新空间和旧空间虽然是两块独立的空间但是呢每个对象中的_str都和另一个对象指向了那一块同样的空间 那么在接下去在执行这句代码的时候就会先去调用当前对象的析构函数将每一块空间中的内容先清理掉然后再去调用delete释放掉整块空间。因为每两个对象所指向的空间都是同一块的所以在释放的时候就会造成同时修改的问题 delete[] _start;【总结一下】 vector是深拷贝但是vector空间上存的对象是string的数组使用memcpy()导致string对象的浅拷贝 那我们要如何去避免这一种问题呢 很简单我们去换一下这个拷贝的逻辑就可以了不要使用memcpy()去进行浅拷贝而是使用下面这种形式去进行拷贝 对于tmp[i] _start[i]如果对代码比较敏感的同学应该可以很快地看出这会去调用 string类 的赋值重载然后去做一个深拷贝此时就不会造成两个_str指向同一块空间了 for (size_t i 0; i size(); i) {tmp[i] _start[i]; }最后通过调试再来观察一下 以下就是【reserve】这个接口的最终完整版实现逻辑 void reserve(size_t n) {if (n capacity()){// 先保存一下原先的size()size_t sz size();T* tmp new T[n]; // 开一块新空间if (_start){//memcpy(tmp, _start, sizeof(T) * size());for (size_t i 0; i size(); i){tmp[i] _start[i];}delete[] _start;}_start tmp;_finish _start sz;_end_of_storage _start n;} }接下去的话我们再来看看【resize】这个接口该如何去实现 还是一样分为三类来进行讨论 一个是n _finish的情况第二个是n _finish n _end_of_storage的情况第三个是n _end_of_storage的情况 对于后两种情况我们可以做一个合并使用上面【reserve】去做一个容量的检查 我们来看一下具体的代码首先是第一种直接让_finish _start n即可如果是另一种情况的话就先使用【reserve】去检查一下是否需要扩容然后再去通过循环追加对应的数据即可 void resize(size_t n, const T val T()) {if (n size()){_finish _start n;}else{// 先使用reserve()去检查一下是否需要扩容reserve(n);while (_finish ! _start n){*_finish val;_finish;}} }可能有同学比较好奇这个T()是干嘛的还记我们在 C缺省参数 中所讲到的知识点吗。没错这个T()就是给到的默认缺省参数因为当前的形参【val】的类型使用的就是模版参数类型采取自动推导的形式去进行自动识别而T()就是我们在 类和对象小知识 中所学习过的【匿名对象】切记这里不可以给0因为当前的数据类型不一定就是 整型我们就可以根据这个匿名对象去生成不同的默认值 const T val T()简单地来测试一下 3、元素访问 对于元素访问的话我们最常用的就是下标 []的形式这里给出两种一个是const版本和非const版本 T operator[](size_t pos) {assert(pos size());return _start[pos]; }T operator[](size_t pos) const {assert(pos size());return _start[pos]; }4、修改操作 接下去的话我们来讲讲有关修改操作的一些接口 首先第一个的话就是push_back这个我在上面讲【reserve】的时候给出过现在仔细地再来讲一讲首先的话我们要考虑的就是扩容的逻辑上面我们有讲到在VS下是呈现 1.5倍 的增长趋势但是在g下呈现的则是 2倍 的扩容逻辑这里的扩容的话我们就交给【reserve】来实现 void push_back(const T x) {if (_finish _end_of_storage){size_t newCapacity capacity() 0 ? 4 : capacity() * 2;reserve(newCapacity);}*_finish x;_finish; }然后的话我们来实现一下【insert】这个接口 void insert(iterator pos, const T x)这一块的话我们已经讲过很多遍了要在某一个位置插入数据的话就需要先去挪动部分的数据这里我们从后往前挪防止造成覆盖的情况当数据挪动完毕后再在pos这个位置插入指定的数据即可 在一进入函数的时候大家可以去做一个断言的操作不过很多同学可能会好奇这边的pos _start为什么可以位于首部 assert(pos _start pos _finish);在讲解 string类 的时候我们确实讲到了这种写法的缺陷但是读者要看清楚了这里pos的类型是 iterator为一个迭代器。而我们在 string类 中所讲到的这个pos呢是一个无符号整数 位于首部的迭代器pos不可能是0因为它是一段空间的地址有效空间的地址不可能是0 string insert (size_t pos, const string str);不过呢既然是插入数据的话就一定会存在容量不足的情况此时就需要一个扩容逻辑这里我们直接用上面在push_back()接口中所写的即可 // 1.首先考虑扩容逻辑 if (_finish _end_of_storage) {size_t newCapacity capacity() 0 ? 4 : capacity() * 2;reserve(newCapacity); }以下是整体的代码 void insert(iterator pos, const T x) {assert(pos _start pos _finish);// 1.首先考虑扩容逻辑if (_finish _end_of_storage){size_t newCapacity capacity() 0 ? 4 : capacity() * 2;reserve(newCapacity);}// 2.挪动数据iterator end _finish - 1;while (end pos){*(end 1) *end;--end;}*pos x;_finish; }那么对于push_back()这个接口我们就可以去复用一下【insert】这个接口了 void push_back(const T x) {/*if (_finish _end_of_storage){size_t newCapacity capacity() 0 ? 4 : capacity() * 2;reserve(newCapacity);}*_finish x;_finish;*/insert(end(), x); }第三轮测试 — 迭代器失效问题 好在写完【insert】接口后我们再来做一个测试。可以发现程序崩溃了 马上我们通过调试来观察一下 此时我们已经往【v】中插入了4个数据马上使用insert(v.begin(), 100)去做一个头插那么一进到函数中我们就可以知道这个当前对象的_start和pos所处的迭代器位置是相同的也就是同一段空间的地址 那此时我们知道容器中的空间已经满了所以会去走一个扩容的逻辑此时可以看到当前对象this的_start已经发生了改变 可以看到在扩完容之后当前对象的_start和待插入位置的pos已经发生了变化那么在此时我们再去挪动数据进行插入的时候就会出现问题了 我们可以通过下面的图示来看看到底这个扩完容之后是怎样的 可以看到_start确实发生了一个变化但是呢pos还是指向原来的那个地方。那读者可以自己去想象一下子在遍历挪动数据的时候究竟何时才是个头呢 我们可以通过调试再来观察一下挪动数据这段逻辑可以看到在挪动完几次数据后就出现了随机值并且出现了死循环的问题 以上所出现的这个问题就被称作是 【迭代器失效的问题】 那我们要如何去解决呢 有同学说内部外部无法一起修改的话参数部分加个引用不就行了 void insert(iterator pos, const T x)这其实是不对的因为有些时候我们所传递的迭代器位置是v.begin() 3在这中间会去产生一个临时对象我们知道临时对象是具有常性的那么传递进去的时候就会造成【权限放大】的问题 v.insert(v.begin() 3, 6);那有同学又说那防止一下权限放大不就好了加个const 这肯定是不可以的看到程序依旧会出现问题 首先大家要明白的一个点是出错的根本原因在于_start的位置改变了但是pos的位置没有发生改变。所以我们所要做的一个点就是让pos的位置随着_start的变动而一起变动这样就不会出现问题了。以下我们需要改进的代码部分在进行扩容之前我们可以先去计算一下从【_start】到【pos】的位置有多远然后呢我们在执行完扩容的逻辑之后就要去更新一下这个【pos】迭代器的位置所在就使用刚才计算出来的这段距离即可 // 1.首先考虑扩容逻辑 if (_finish _end_of_storage) {// 首先保存一下从_start到pos的距离size_t len pos - _start;size_t newCapacity capacity() 0 ? 4 : capacity() * 2;reserve(newCapacity);// 再扩完容之后更新一下pos, 解决迭代器失效问题pos _start len; }那代码做了更新之后迭代器失效的问题真的解决了呢我们通过调试一起来看看 可以看到通过我们的骚操作【pos】位置就随着【start】的变化而随着变化 但是呢就上面这样还不够我们只解决了内部迭代器失效的问题而外部迭代器失效的问题并没有很好地解决。 外部迭代器那是什么东西 我们来看下这段代码 bit::vectorint::iterator it v.begin(); v.insert(it, 33); bit::print(v);cout *it endl;bit::print(v);可以看到在使用完这个这个迭代器之后再去访问就出现了问题 如果直接其换成库里面的【vector】的话就直接崩溃了 所以对于迭代器这一块我们在使用的时候一定要慎重在使用完之后不要去轻易地修改它 it v.insert(it, 33);如何执意要进行修改的话也不是没有办法我们只需要在【insert】之后去接受一下当前所操作的这个迭代器的位置即可记住这个位置下次在访问的时候也就不会出问题 具体代码如下 iterator insert(iterator pos, const T x) {assert(pos _start pos _finish);// 1.首先考虑扩容逻辑if (_finish _end_of_storage){// 首先保存一下从_start到pos的距离size_t len pos - _start;size_t newCapacity capacity() 0 ? 4 : capacity() * 2;reserve(newCapacity);// 再扩完容之后更新一下pos, 解决迭代器失效问题pos _start len;}// 2.挪动数据iterator end _finish - 1;while (end pos){*(end 1) *end;--end;}*pos x;_finish;return pos; }有【insert】那一定少不了【erase】我们继续来看看 对于【erase】来说我们也是需要先去挪动数据的但是在这里呢我们需要从前往后挪也是防止造成覆盖的情况 具体代码如下 void erase(iterator pos) {assert(pos _start pos _finish);iterator end pos 1;// 移动覆盖while (end ! _finish){*(end - 1) *end;end;}--_finish; }立马来测试一下 对于【insert】来说会存在迭代器失效的问题那对【erase】来说也会有吗 我们立马通过下面的代码来测试一下 void test_vector8() {bit::vectorint v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);bit::print(v);auto it v.begin();v.erase(it);cout *it endl;it;cout *it endl;bit::print(v); }运行起来我们可以看到并没有出现任何的问题首先去删除了第一个元素然后再访问到首个位置便是【2】接下去再去执行it并访问的话便是【3】 但若是我们将代码换成下面这样的话就会出现问题了 auto it v.begin() 3;运行起来可以看到当我们删除完最后一个元素后再让迭代器后移就造成了访问越界的问题出现了随机值的情况 不过呢上面只是我们使用自己模拟的【vector】来用用库里的会看会发生什么情况 但是呢我们可以看到如果使用库里面的话就会直接造成程序崩溃的问题 上面呢是在VS下的运行结果之前有说过VS在的STL是【PJ版】而Linux下则是【SGI版】所以我们都要去做一个对比 可以看到神奇的事情发生了~ 在Linux下执行同样的两段代码却没有发生像VS里面那样的报错甚至在访问越界之后也没有出现随机值的问题而是【0】 【小结】 erase以后迭代器失效了不能访问。VS进行强制访问会直接报错Linux下则不会 然后我们再来看一个点 下面这个场景是通过迭代器的形式去删除其中的偶数 auto it v.begin(); while(it ! v.end()) {if(*it % 2 0){v.erase(it);}it; }通过运行结果我们可以看出确实所有的偶数都被删除了 换一个测试用例我们加一个【2】然后在删除的时候就发现【2】没有被删干净 再换一个测试用例我在最后加了一个【6】运行之后发现报出了Segmentation fault这是Linux下的段错误问题 我们通过画图来分析一下 首先是对于第二种根据代码来进行走读当我们删除了第一个【2】后后面的四个元素就往前移动了一个位置接着迭代器后移就来到了【3】的位置所以就错过了第2个【2】 那对于第三种测试案例因为最后一个是 偶数 的原因所以在删除之后迭代器进行了后移此时呢它已经是越过了end()的位置再去判断的话永远都到不了所以就出现了【Segmentation fault】的问题 那要如何去避免呢 这其实很简单我们不要让这个迭代器每次都后移就可以了 auto it v.begin(); while(it ! v.end()) {if(*it % 2 0){v.erase(it);}else{ it;} }再去打印看一下看看就发现没什么问题了 但是呢这段代码如果放到VS上去的话就不一样了在Linux下确实是不会出现什么问题但是在VS下还是一样会直接报错因为VS会进行强制检查在访问了一次迭代器之后就不可以再继续访问了 此时我们需要去考虑一下【erase】这个接口的详情了 我们要看的是这个返回值其返回值是一个迭代器而且是刚刚被删除那个元素的下一个位置 iterator erase (const_iterator position);那如果是这样的话我们就可以考虑在每次删除完一个位置的数据后拿返回值接收一下这个所删除元素的下一个位置那么在下一次继续访问的时候就不会造成修改操作的问题了 it v.erase(it);最后【erase】接口的整体代码如下所示 iterator erase(iterator pos) {assert(pos _start pos _finish);iterator end pos 1;// 移动覆盖while (end ! _finish){*(end - 1) *end;end;}--_finish;return pos; }在有了【erase】之后我们就可以让pop_back()去复用这个接口了可以达到尾删的逻辑 void pop_back() {// 复用eraseerase(end() - 1); }最后的话再来讲讲【swap】这个接口很简单就是去调用库里面的这个模版函数swap去一一交换两个对象中的三个成员变量即可。这个接口我下面在讲【赋值重载】时会使用到 void swap(vectorT v) {std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage); }5、默认成员函数 讲了这么多终于能来讲讲默认的成员函数了 首先的话一定是构造函数有参构造是一定要实现的因为这里的逻辑和resize()是类似的因此我们直接去做一个复用即可 // 有参构造 vector(size_t n, const T val T()) {resize(n, val); }那我们在构造的时候就可以去做一个初始化了发现和v.resize(10, 0)是同样的效果 bit::vectorint v(10, 0);那有同学可能会问三个私有成员变量不需要去做初始化吗 同学难道你忘了我们在一开始的时候已经给到了它们初始值为nullptr吗这个措施就是很好地避免编译器对内置类型不会去做初始化的问题 private:iterator _start nullptr;iterator _finish nullptr;iterator _end_of_storage nullptr;除了上面这种初始化我再介绍一种方法那就是使用 迭代器区间 这里我们可以去使用循环配合【push_back】接口去做一个初始化 // [first, last) templateclass InputIterator vector(InputIterator first, InputIterator last) {while (first ! last){push_back(*first);first;} }第四轮测试 — 双重构造引发调用歧义 接下去我们马上对这个迭代器区间做的初始化操作去所一个测试 void test_vector6() {bit::vectorint v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);bit::vectorint v2(v.begin(), v.end());string s(abcdef);bit::vectorint v2(s.begin(), s.end());int a[] { 1,2,3,4 };bit::vectorint v2(a, a 4); }可以看到除了去初始化自己【vector】对象的迭代器区间【string】对象也可以而且指针也没问题 但此时呢如果我再去以下面的有参构造进行初始化的话就会出现一些问题 bit::vectorint v5(10, 1);可以看到说是“非法的间接寻址” 这里对迭代器first去进行解引用目的就是为了获取这个位置上的数据我们在 指针一文 有所提到 只有指针和迭代器可以解引用基本数据类型不能解引用 但是有同学一定会疑惑说为什么这里不会去匹配有参构造而是去匹配的迭代器区间构造呢 在讲 C模版 的时候我们有说到过模版参数会去进行自动类型推导从而匹配最合适函数模版。因为我们在这里所传入的【10】和【1】都是int类型但是呢有参构造的第一个形参类型为size_t并不是最匹配的 而迭代器区间初始化其参数类型都是模版参数所以在匹配的时候它是最优先进行匹配的 那我们该如何去进行预防呢 很简单我们可以利用在 C重载函数 中所学习的参数类型不同去另写一个有参构造的重载形式 vector(size_t n, const T val T()) {resize(n, val); }vector(int n, const T val T()) {resize(n, val); }通过调试我们可以看出这里在调用的时候就没有歧义了 最后再补充一个小的知识点作为拓展 那我们在写了这个重载函数后要如何去调用对应的无符号类型size_t呢此时我们只需要在传递的参数后加上一个u即可那么编译器在进行识别的时候就会自动将其识别成为无符号整数 bit::vectorint v6(10u, 6);一样通过调试来看就可以很清楚 讲完构造函数了我们来看看拷贝构造 首先读者要明确为什么要写拷贝构造这个我们通过调试来看一下就知道了很明显可以看到这里只是做了一个浅拷贝而不是去做了深拷贝 所以我们要自己去实现一个深拷贝逻辑很简单就不赘述 // 拷贝构造 vector(vectorint v) {_start new T[v.capacity()];memcpy(tmp, v._start, sizeof(T) * v.size());_finish tmp v.size();_end_of_storage tmp v.capacity(); }但是看到上面这个memcpy()你是否会有一种警惕的心理呢因为我们上面讲到过 vector 对象中存放的是 string数组在拷贝的过程中会产生浅拷贝的问题那就不可以去使用这个memcpy()具体问题间下图 所以拷贝构造的正确形式应该是下面这样的 // 拷贝构造 vector(vectorT v) {_start new T[v.capacity()];//memcpy(_start, v._start, sizeof(T) * v.size());for (size_t i 0; i v.size(); i){_start[i] v._start[i];}_finish _start v.size();_end_of_storage _start v.capacity(); }可以看到在改成深拷贝后就不会出现类似的问题了 当然我们也可以去做一个投机取巧复用当前已经实现过的接口【reserve】和【push_back】首先根据所传递进来对象的容量去做一个扩容的逻辑开出足够多的空间后再将这个对象中的数据一一尾插进当前对象即可 vector(vectorint v) {// 根据v的capacity()去开出对应的空间reserve(v.capacity());for (size_t i 0; i v.size(); i){push_back(v[i]);} }有了拷贝构造【赋值重载】也少不了 还记得我们在上面所实现过的【swap】接口吗在进行 string模拟 的时候我们又使用到这么一个巧计那就是使用 传值传参首先会去调用一个拷贝构造构造一个临时对象但是临时对象出了作用域之后肯定是要销毁的此时我们就可以使用【swap】和当前对象去做一个交换我呢获取到了你里面的内容你帮我释放了不需要的内容简直一举两得还记得PUA弟弟的故事吗 // 赋值重载 const vectorT operator(vectorT v) {swap(v);return *this; }好我们来调试观察一下。看到在调用赋值重载前就会去 调用拷贝构造 最后的舞台给到【析构函数】再怎么花里胡哨最后最后空间都是要还给操作系统的 ~vector() {delete[] _start;_start _finish _end_of_storage nullptr; }OK以上就是有关vector深度剖析及模拟实现希望对您有帮助
http://www.zqtcl.cn/news/87145/

相关文章:

  • 做房地产公司网站的费用管理软件开发平台
  • 济南建设网站公司哪个好seo内部优化包括哪些内容
  • 网站安全检测在线建设设计公司网站
  • 属于网页制作工具宁波做seo推广企业
  • 织梦绿色企业网站模板 苗木企业网站源码 dedecms5.7内核口腔网站建设
  • 英国网站后缀有声直播网站建设
  • 建网站工具微信网页版客户端
  • 前端网站页面模板下载我的家乡网页设计模板
  • 响应式网站代理网站建设外出考察报告
  • 自己做的网站被封了广州软件制作公司
  • 为什么我做的网站不是加密访问小江高端企业网站建设
  • 网站做支付需要准备什么条件大连网站运营制作方案
  • 全国建设项目验收信息网站3d建模培训机构排行榜
  • 站长工具使用网站建设内部下单流程图
  • 史志网站建设wordpress模版头部文件
  • 苏州优化网站排名sem培训班学费哪个好
  • 关于新闻管理的网站建设报告自己做网站现实么
  • 做网站要营业执照吗什么网站上可以做国际贸易
  • 门户网站建设验收报告网络推广怎么收费
  • 鲜花网站建设主要内容网站关键词优化排名软件
  • 广州企业网站seo网站html地图模板
  • html字体代码大全上海seo推广服务
  • 南阳专业网站建设专业网站推广公司
  • 网站开发培训实训上海计算机一级网页制作
  • 甘德县公司网站建设英语培训东莞网站建设
  • 网站的登陆注册页面怎么做淘宝做导航网站有哪些功能
  • 江苏网站建设效果长治哪里做网站
  • 手机网站如何更改大品牌网站建设
  • 天眼查河南建设网站公司兰州中川国际机场t3航站楼
  • seo站内优化世界足球排名前100