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

免费asp网站程序下载竞价推广开户

免费asp网站程序下载,竞价推广开户,软装设计案例网站,河南网站建设设计欢迎阅读我的 【CPrimer】专栏 专栏简介:本专栏主要面向C初学者,解释C的一些基本概念和基础语言特性,涉及C标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级…

欢迎阅读我的 【C++Primer】专栏

专栏简介:本专栏主要面向C++初学者,解释C++的一些基本概念和基础语言特性,涉及C++标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级程序设计技术。希望对读者有帮助!

在这里插入图片描述
在这里插入图片描述

目录

  • 13.5动态内存管理类
    • StrVec类的设计
    • StrVec类定义
    • 使用construct
    • free成员
    • 拷贝控制成员
    • 在重新分配内存的过程中移动而不是拷贝元素
    • 移动构造函数和std::move
    • reallocate成员

13.5动态内存管理类

某些类需要在运行时分配可变大小的内存空间。这种类通常可以(并且如果它们确实可以的话,一般应该)使用标准库容器来保存它们的数据。例如,我们的StrBlob类使用一个vector来管理其元素的底层内存。

但是,这一策略并不是对每个类都适用;某些类需要自己进行内存分配。这些类一般来说必须定义自己的拷贝控制成员来管理所分配的内存。

例如,我们将实现标准库vector类的一个简化版本。我们所做的一个简化是不使用
模板,我们的类只用于string。因此,它被命名为StrVec。

StrVec类的设计

回忆一下,vector类将其元素保存在连续内存中。为了获得可接受的性能,vector预先分配足够的内存来保存可能需要的更多元素。vector的每个添加元素的成员函数会检查是否有宇间容纳更多的元素。如果有,成员函数会在下一个可用位置构造一个对象。如果没有可用空间,vector就会重新分配空间:它获得新的空间,将已有元素移动到新空间中,释放旧空间,并添加新元素。

我们在StrVvec类中使用类似的策略。我们将使用一个allocator来获得原始内存。由于allocator分配的内存是未构造的,我们将在需要添加新元素时用allocator的construct成员在原始内存中创建对象。类似的,当我们需要删除一个元素时,我们将使用destroy成员来销毁元素。

每个StrVec有三个指针成员指向其元素所使用的内存:

  • elements,指向分配的内存中的首元素
  • first_free,指向最后一个实际元素之后的位置
  • cap,指向分配的内存末尾之后的位置

除了这些指针之外,StrVec还有一个名为alloc的静态成员,其类型为allocator。alloc成员会分配StrVec使用的内存。我们的类还有4个工具函数:

alloc_n_copy会分配内存,并拷贝一个给定范围中的元素。free会销毁构造的元素并释放内存。

*chk_n_alloc保证StrVec至少有容纳一个新元素的空间。如果没有空间添加新元素, reallocate在内存用完时为StrVec分配新内存。

虽然我们关注的是类的实现,但我们也将定义vector接口中的一些成员。

StrVec类定义

有了上述实现概要,我们现在可以定义StrVec类,如下所示:

//类vector类内存分配策略的简化实现class StrVec {
public:
StrVec()://allocator成员进行默认初始化
elements(nullptr),first_free(nullptr),cap(nullptzr){}StrVec(constStrVecg)}//拷贝构造函数
StrVec&operator=(const StrVec&)}//拷贝赋值运算符
~StrVec();//析构函数void push_back(const std::string&);//拷贝元素
size_t size()const{return first_free - elements;}
size_t size_capactty()const{return cap - elements;}
std::string*begin()const {return elements;}
std::string*end()const{return first_free;}private:
static std::allocator<std::string>alloc;//分配元素
//被添加元素的函数所使用
void chk_n_alloc()
{if(size()==capacity()) reallocate();//工具函数,被拷贝构造函数、赋值运算符和析构函数所使用std::pair<std::string,std::string*>alloc_n_copy(conststd::string*,conststd::string*);void free();// 销毁元素并释放内存void reallocate();//获得更多内存并拷贝已有元素std::string*elements;//指向数组首元素的指针std::string*first_free;//指向数组第一个空闲元素的指针std::string*cap;//指向数组尾后位置的指针
}

类体定义了多个成员:

  • 默认构造函数(隐式地)默认初始化alloc并(显式地)将指针初始化为nullptr,表明没有元素。
  • size成员返回当前真正在使用的元素的数目,等于ftrst_free-elements。
  • capacity成员返回StrVec可以保存的元素的数量,等价于cap-elements。
  • 当没有空间容纳新元素,即cap==first_free时,chk_n_alloc会为StrVec重新分配内存。
  • begin和end成员分别返回指向首元素(即elements)和最后一个构造的元素
    之后位置(即first_free)的指针。

使用construct

函数push_back调用chk_n_alloc确保有空间容纳新元素。如果需要,chk_n_alloc会调用reallocate。当chk_n_alloc返回时,push_back知道必有空间容纳新元素。它要求其allocator成员来construct新的尾元素:

void StrVec::push_back(const string&s)
{chk_n_alloc();//确保有空间客纳新元素// 在first_free指向的元素中构造s的副本alloc.construct(first_free++,s)
}

当我们用allocator分配内存时,必须记住内存是未构造的。为了使用此原始内存,我们必须调用constzuct,在此内存中构造一个对象。传递给construct的第一个参数必须是一个指针,指向调用allocate所分配的未构造的内存空间。剩余参数确定用哪个构造函数来构造对象。在本例中,只有一个额外参数,类型为string,因此会使用string的拷贝构造函数。

值得注意的是,对construct的调用也会递增first_freey表示已经构造了一个新元素。它使用前置递增,因此这个调用会在first_free当前值指定的地址构造一个对象,并递增first_free指向下一个未构造的元素。

alloc_n_copy成员

我们在拷贝或赋值StrVec时,可能会调用alloc_n_copy成员。类似vector,我们的StrVec类有类值的行为。当我们拷贝或赋值StrVec时,必须分配独立的内存,并从原StrVec对象拷贝元素至新对象。

alloc_n_copy成员会分配足够的内存来保存给定范围的元素,并将这些元素拷贝到新分配的内存中。此函数返回一个指针的pair,两个指针分别指向新空间的开始位置和拷贝的尾后的位置:

pair<string*,string*>
StrVec::alloc_n_copy(conststring*b,conststring*e)
{//分配空间保存给定范围中的元素auto data=alloc.allocate(a-b);//初始化并返回一个pair,该pair由data和uninitialized_copy的返回值构成return〔data,uninitialized_copy(b,e,data)};
}

alloc_n_copy用尾后指针减去首元素指针,来计算需要多少空间。在分配内存之后,它必须在此空间中构造给定元素的副本。

它是在返回语句中完成拷贝工作的,返回语句中对返回值进行了列表初始化。返回的pair的first成员指向分配的内存的开始位置;second成员则是uninitialized_copy的返回值,此值是一个指针,指向最后一个构造元素之后的位置。

free成员

free成员有两个责任:首先destroy元素,然后释放StrVec自己分配的内存空间。for循环调用allocator的destroy成员,从构造的尾元素开始,到首元素为止,逆序销毁所有元素:

void StrVec::free()
{//不能传递给deallocate一个空指针,如果elements为0,函数什么也不做if(elements){//递序销毁旧元素for(autoP=first_free;p!=elements;/*空*/)alloc.destroy(--p);alloc.deallocate(elements,cap-elements);}
}

destroy函数会运行string的析构函数。string的析构函数会释放string自己分配的内存空间。

一旦元素被销毁,我们就调用deallocate来释放本StrVec对象分配的内存空间。我们传递给deallocate的指针必须是之前某次allocate调用所返回的指针。因此,在调用deallocate之前我们首先检查elements是否为空。

拷贝控制成员

实现了alloc_n_copy和free成员后,为我们的类实现拷贝控制成员就很简单了。

拷贝构造函数调用allocn_copy:

StrVec::StrVec(const_StrVec&s)
{//调用alloc_n_copy分配空间以客纳与s中一样多的元素autonewdata=alloc_n_copy(s.begin(),s-.end());elements=newdata.first;first_free=cap=newdata.second;
}

并将返回结果赋予数据成员。alloc_n_copy的返回值是一个指针的pairz。其first成员指向第一个构造的元素,second成员指向最后一个构造的元素之后的位置。由于alloc_n_copy分配的家间恰好容纳给定的元素,cap也指向最后一个构造的元素之后的位置。

析构函数调用free:

StrVec::~StrVec(){free();}

拷贝赋值运算符在释放已有元素之前调用alloc_n_copy,这样就可以正确处理自赋值了

StrVec& StrVec::operator=(const StrVec& rhs)
{//调用alloc_n_copy分配内存,大小与rhs中元素占用空间一样多auto data = alloc_n_copy(rhs.begin(),rhs.end());free();elements = data.first;first_free = cap = data.second;return *this;
}

类似拷贝构造函数,拷贝赋值运算符使用alloc_n_copy的返回值来初始化它的指针。

在重新分配内存的过程中移动而不是拷贝元素

在编写reallocate成员函数之前,我们稍微思考一下此函数应该做什么。它应该

  • 为一个新的、更大的string数组分配内存
  • 在内存空间的前一部分构造对象,保存现有元素
  • 销毁原内存空间中的元素,并释放这块内存

观察这个操作步骤,我们可以看出,为一个StrVec重新分配内存空间会引起从旧内存空间到新内存空间逐个拷贝string。虽然我们不知道string的实现细节,但我们知道string具有类值行为。当拷贝一个string时,新string和原string是相互独立的。改变原string不会影响到副本,反之亦然。

由于string的行为类似值,我们可以得出结论,每个string对构成它的所有字符都会保存自己的一份副本。拷贝一个string必须为这些字符分配内存空间,而销毁一个string必须释放所占用的内存。

拷贝一个string就必须真的拷贝数据,因为通常情况下,在我们拷贝了一个string之后,它就会有两个用户。但是,如果是reallocate拷贝StrVec中的string,则在拷贝之后,每个string只有唯一的用户。一旦将元素从旧空间拷贝到了新空间,我们就会立即销毁原string。

因此,拷贝这些string中的数据是多余的。在重新分配内存空间时,如果我们能邀免分配和释放string的额外开销,StrVec的性能会好得多。

移动构造函数和std::move

通过使用新标准库引入的两种机制,我们就可以避免string的拷贝。首先,有一些标准库类,包括string,都定义了所谓的“移动构造函数“。关于string的移动构造函数如何工作的细节,以及有关实现的任何其他细节,目前都尚未公开。但是,我们知道,移动构造函数通常是将资源从给定对象“移动“而不是拷贝到正在创建的对象。而且我们知道标准库保证“移后源“(moved-from)string仍然保持一个有效的、可析构的状态。对于string,我们可以想象每个string都有一个指向char数组的指针。可以假定string的移动构造函数进行了指针的拷贝,而不是为字符分配内存空间然后拷贝字符。

我们使用的第二个机制是一个名为move的标准库函数,它定义在utility头文件中。目前,关于move我们需要了解两个关键点。首先,当reallocate在新内存中构造string时,它必须调用move来表示希望使用string的移动构造函数。如果它漏掉了move调用,将会使用string的拷贝构造函数。其次,我们通常不为move提供一个using声明。当我们使用move时,直接调用std::move而不是move。

reallocate成员

了解了这些知识,现在就可以编写reallocate成员了。首先调用allocate分配新内存空间。我们每次重新分配内存时都会将StrVec的容量加借。如果StrVec为空,我们将分配容纳一个元素的空间:

void StrVec::reallocate()
{//我们将分配当前大小两倍的肉存空间auto newcapacity = size()?2*size():1//分配新内存auto newdata = alloc.allocate(newcapacity);//将数据从旧内存移动到新内存auto dest=newdata//指向新数组中下一个空闵位置auto elem=elements;//指向旧数组中下一个元素for(size_t i=0;i!=size();++i){alloc.construct(dest++,std::move(*xelem++));}free();//一旦我们移动完元素就释放旧内存空间//更新我们的数据结构,执行新元素elements=newdata;first_free= dest;cap = elements + newcapacity;
}

for循环遍历每个已有元素,并在新内存空间中construct一个对应元素。我们使用dest指向构造新string的内存,使用elem指向原数组中的元素。我们每次用后置递增运算将dest(和elem)推进到各自数组中的下一个元素。

construct的第二个参数是move返回的值。调用move返回的结果会令construct使用string的移动构造函数。由于我们使用了移动构造函数,这些string管理的内存将不会被拷贝。相反,我们构造的每个string都会从elem指向的string那里接管内存的所有权。

在元素移动完毕后,我们调用free销毁旧元素并释放StrVec原来使用的内存。string成员不再管理它们曾经指向的内存;其数据的管理职责已经转移给新StrVec内存中的元素了。我们不知道旧strVec内存中的string包含什么值,但我们保证对它们执行string的析构函数是安全的。

剩下的就是更新指针,指向新分配并已初始化过的数组了。first_free和cap指针分别被设置为指向最后一个构造的元素之后的位置及指向新分配空间的尾后位置。

http://www.dtcms.com/wzjs/243335.html

相关文章:

  • 北京网站建设华大网络营销策划推广公司
  • 天津建设厅官方网站网络竞价
  • 最好的网站建设哪家好批量查询指数
  • 城乡建设与管理委员会网站下载百度极速版免费安装
  • 简述网站首页制作过程教育机构培训
  • 怎么看一个网站是不是外包做的北京疫情太严重了
  • 台州seo网站排名杭州seo靠谱
  • 公司弄个网站多少钱重庆森林壁纸
  • 网站建设要与安全防护同步规划网络工具
  • 微信赌博链接网站建设北京网站优化常识
  • 做招聘图片的网站百度关键词价格计算
  • 哪里可以接一些网站项目做湖州网站seo
  • 在百度上做网站多少钱完美日记网络营销策划书
  • mysql做网站怎么查看数据外贸网站建设平台
  • 怎么做代理人金沙网站友链交换有什么作用
  • 没有域名可以建网站吗sem是什么电镜
  • 套别人代码做网站2022年新闻摘抄简短
  • 哪个网站做推销产品seo人员培训
  • 延安网站建设公司电话企业管理软件
  • 定制化网站开发互动营销的案例及分析
  • 连云港做网站哪里好网站推广优化服务
  • 美妆企业网站模板关键词林俊杰mp3免费下载
  • 宁波公司建网站哪家好网络推广员一个月多少钱
  • 1688开山网一起做网站百度数字人内部运营心法曝光
  • 在线快速建站网络营销软文案例
  • 小语种网站制作苏州百度推广公司
  • 两学一做山西答题网站网站策划方案书
  • 湘潭网站建设公司有哪些b2b国际贸易平台
  • 日报社网站平台建设项目朝阳区搜索优化seosem
  • 做python题目的网站nba最新消息球员交易