浙江省建设工程监理管理协会网站网页界面设计是什么

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

浙江省建设工程监理管理协会网站,网页界面设计是什么,甘肃省最新消息今天,网络营销中自建网站文章目录 使用STL库的55条建议1.慎重选择容器的类型2.不要试图编写独立于容器的代码3.确定容器中的对象拷贝正确且高效4.调用empty判断是否为空#xff0c;而不是size5.区间成员函数优于与之对应单元素成员函数6.如果容器中包含了通过new操作创建的指针#xff0c;切记在容器… 文章目录 使用STL库的55条建议1.慎重选择容器的类型2.不要试图编写独立于容器的代码3.确定容器中的对象拷贝正确且高效4.调用empty判断是否为空而不是size5.区间成员函数优于与之对应单元素成员函数6.如果容器中包含了通过new操作创建的指针切记在容器对象析构前要将指针delete。7.切勿创建包含auto_ptr的容器对象8.慎重选择删除元素的方法9.了解分配子allocator的约定和限制10.切勿对STL容器的线程不安全性有不切实际的依赖11.vector和string优于动态分配的数组12.使用reserve避免不必要的重新分配13.注意string实现的多样性14.了解如何将string和vector 传给旧的API15.使用‘swap技巧’去除多余的内存16.避免使用vectorbool17理解相等和等价的区别18.为包含指针的关联函数指定比较类型19.总让比较函数在相等的情况下返回false20.切勿直接修改set 或者 multiset的键21.考虑用排序vector代替关联容器22.当效率至关重要谨慎选择opertor[] 和insert23.使用distance 和advance将容器的const_iterator转换为iterator24.正确理解由reverse_iterator的base()成员函数所产生的iterator的用法25.对于逐字符的输入请考虑使用istreambuf_iterator26.确保目标区间足够大27.了解各种和排序有关的选择28.如果确实需要删除元素则需要在remove这一类算法之后调用erase29.使用accumulate进行区间统计30.遵循按值传递的原则来设计函数子类31.确保判别式是纯函数32.若一个类是函数子应该让它可配接33.理解ptr_fun,mem_fun和mem_fun_ref的由来34.算法调用优于手写的循环35.容器的成员函数优于同名算法35.正确区分count,find,binary_search,lower_bound,upper_bound和equal_range36.避免直写型的代码37.总是包含#include正确的头文件36.避免直写型的代码37.总是包含#include正确的头文件 使用STL库的55条建议 1.慎重选择容器的类型 根据你的业务需求和算法去选择不同的容器将数据结构和算法结合起来。 c容器类型 序列容器其中对象有序排列根据整数值进行索引,有vector,list,deque,string关联容器其中对象的顺序不重要根据键值进行索引,set,map,multiset,multimap适配器调正原有容器的行为使对外产生新的接口类型和返回元素。生成器构造元素序列 标准容器:指的是c标准化后的容器遵从c的标准不同的编译器都支持代码移植能力强。vector,list,deque,stringset,map,multiset,multimap 非标准容器:指的是违背标准化后的容器在不同版本的编译器上可能不支持,hash_set,hast_map等等 使用参考 例如需要按顺序插入元素就需要序列容器如果元素的顺序不重要就使用关联容器如果就频繁删除容器内元素就避免使用内存连续的容器 2.不要试图编写独立于容器的代码 因为不同的容器所支持的函数和迭代器类型使不同的可能会报错所以不要编写独立于容器的代码。 3.确定容器中的对象拷贝正确且高效 当对vector,list,deque进行元素的插入或者删除操作时现有的元素的位置通常会被移动拷贝。使用排序反转也会被移动拷贝。拷贝是STL的工作方式。 当给容器中存入对象时存入的是对象的拷贝当从容器中取出对象时取出的容器中对象的拷贝。所以要确保拷贝的正确和高效。 当你创建的时基类对象却存着派生类对象时拷贝就会剥离将派生的内容剥离。如果想要从基类容器使用派生类的虚方法就要指针。 而且拷贝指针。 由于是拷贝的因此在最后析构的时候容易出问题因此最好采用智能指针。 4.调用empty判断是否为空而不是size 首先empty一般是内联函数而且empty的时间复杂度是常数。 size不是内联函数而且size的时间复杂度是线性的。 5.区间成员函数优于与之对应单元素成员函数 给定v1和v2两个矢量使v1的内容等于v2后半段内容相同。怎么实现。 //1.使用区间函数 v1.assign(v2.begin()v2.size()/2,v2.end()); //2.使用copy函数内部还是循环 v1.clear(); copy(v2.begin()v2.size()/2,v2.end(),back_inserter(v1)); //3.使用insert,这样v1是反着的因此每次都插到v1前面 insert(v1.begin(),v2.begin()v2.size()/2,v2.end()); //使用单元素方法 //1.使用赋值 v1.clear() for(vectorint::const_iterator civ2.begin()v2.size()/2;ci!v2.end()ci){v2.push_back(ci); }从代码上 使用区间函数可以少写一些代码使用区间函数意图更明确。 //将数组data内的元素添加到向量v中 //使用单元素成员函数 vectorint::iterator insertloc(v.begin()); for(int i0;inumValues;i){insertlocv.insert(insertloc,data[i]);insertloc; }从效率上调用单元素成员函数 不必要的函数调用将v.insert()函数调用numValues遍将v中已有元素频繁的移动到插入元素的后面每个元素移动numValues遍如果插入元素为对象的化进行拷贝复制对象内的参数需要的赋值次数更多。如果容器为顺序存储的容器则可能会造成内存的多次扩充则会把旧内存的元素拷贝进入新内存并将旧内存的东西销毁然后插入新内存的东西。 对于vector 和string这三条都满足对于deque第三条不满足。 对于list只有第一条满足但是单个插入list会导致list需要不断变化pre 和next的指向没有必要。 6.如果容器中包含了通过new操作创建的指针切记在容器对象析构前要将指针delete。 void dosomething(){ vectorwidget *vmp; for(int i0;iN;i)vwp.push_back(new wideget); } //当vmp作用域结束后它的全部元素会被析构但是new出来的内存没有删除导致内存泄漏//第一版本 void dosomething(){ vectorwidget vmp; for(int i0;iN;i)vwp.push_back(new wideget); } … for(vectorwidget::iterator ivmp.begin();i!vmp.end();i)delete i; //当时当new 过程中出现异常还是会导致内存泄露//第二版本 struct DeleteObject{templatetypename Tvoid operator()(const T *ptr){delete ptr;} }; void dosomething(){typedef boost::shared_ptrwidgetspw;vectorspwvmp; for(int i0;iN;i)vwp.push_back(spw(new wideget); } … for_each(vmp.begin(),vmp.end(),DeleteObject());//将指针给智能指针不怕发生异常内存泄漏然后采用for_each区间比单元素需要要高7.切勿创建包含auto_ptr的容器对象 首先auto_ptr的对象是被c标准所禁止的说明可以移植性不强。 上面提到STL中采用的是拷贝的方法auto_ptr对象进行拷贝时会将原来的对象所有权设置交给新对象然后将原来对象所有权设为为NULL 例如在排序时会将基准元素拷贝给一个临时对象使容器中一个元素为null,临时对象会在作用域结束后被销毁。导致容器可能会失去一个或者多个对象。 8.慎重选择删除元素的方法 containerintc;对于容器c,如果想删除值为1963的元素。对于不同容器应该采用不同的方法。 对于连续容器例如 vector,deque,string c.earse(remove(c.begin(),c.end(),1963),c.end());对于list c.remove(1963);对于关联容器采用 c.erase(1963);当要删除判别式为 true的值 bool badValue(int);对于连续容器例如 vector,deque,string c.earse(remove_if(c.begin(),c.end(),badValue),c.end());对于list c.remove_if(badValue);对于关联容器两种方法 //方法一 AddocContainerintc; … AddocContainerintgoodValues; remove_copy_if(c.begin(),c.end(),inserter(goodValues,goodValues.end(),badValue); //将不用删除的元素放入goodValues容器中 //将两容器元素交换 c.swap(goodVlues;) //方法二 for(AddocContainerint::iterator ic.begin();i!c.end();){if(badValues(*i))c.earse(i); //确保i被删除后有一个迭代器指向下一个元素。关联容器不反悔元素elsei; } //对于 vector,deque,stringlist,想要在为true时写日志for(AddocContainerint::iterator ic.begin();i!c.end();){if(badValues(*i)){logFileearse i/n;i c.earse(i); }//确保i被删除后有一个迭代器指向下一个元素。序列元素返回之下下一个元素的迭代器elsei; }
9.了解分配子allocator的约定和限制 你的分配子是一个模板模板参数T代表你为它分配内存对象的类型提供类型定义pointer和reference,但始终让pointer为T
,让reference 为T千万不要让你的分配子拥有随对象而不同的状态。通常分配子不应该有非静态的数据结构传给分配子的allocate成员函数的是那些要求内存的对象的个数new传递的是void 需要的字节个数。同时记住allocate返回的T*指针既然没有T对象被构造。*一定也要提供嵌套的rebind模板因为标准容器依赖该模板。 10.切勿对STL容器的线程不安全性有不切实际的依赖 STL不提供线程安全需要自己写互斥锁 //定义lock类 templatetyname Container class Lock{ public: Lock(const Container container):c(container){getMutexFor©;//创造互斥体 } ~Lock(){releaseMutexFor©;//析构互斥体 } private: const Container c; }// vectorintv; … {Lockvectorintlock(v);vectorint::iteator first5(find(v.begin(),v.end(),5));if(first5 !v.begin())*first50; }//代码块结束互斥体被释放 11.vector和string优于动态分配的数组 vector和string可以实现自己释放内存并且可以使用STL算法 但是在多线程情况下使用string可能会造成性能下跌不如使用vector因为string采用的是引用计数。 12.使用reserve避免不必要的重新分配 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C8FNfW2Q-1692172443245)(C:\Users\18440\AppData\Roaming\Typora\typora-user-images\image-20220911205652519.png)] 因为当容器容量不满足需求时会进行容量扩充重新申请一个是当前容量数倍的内存把当前内存中元素移入然后将原来的内存进行析构。这个过程很浪费时间因此两种方法去避免。 当知道需要预留的空间大小使用reserve函数提前去开辟。当不知道时开辟足够大的到最后在去除多余容量。 13.注意string实现的多样性 STL对string的实现有不同的方式每个方式都有不同的特点详情看第十五条 它们的区别主要是 string的值可能会被引用也可能不会被引用 string对象的大小范围是char的1倍到7倍 创建一个新字符串可能需要0次12次动态分配内存 string对象可能被共享也可能不共享其大小和内存信息 string可能支持也可能不支持对单个对象的分配子 不同实现对内存的最小分配有不同策略。 14.了解如何将string和vector 传给旧的API 对于vector //对于这个旧的API void dosomething(const int pInts,size_t numInts); //vector 应该传递 vectorintv; dosomething(v[0], numInts); //也有人说传递 dosomething(v.begin(), numInts); //begin()返回是一个迭代器事实上对于vector迭代器就是指针但是事实上迭代器不是一个指针对于string //对于旧的API void dosomething(const char pstring) //strng 应该传递 string str; dosomething(str.c_str()) //因为string不是连续存储的而且string不一定是以空字符结尾的。所以string内部有个c_str指向字符串值的指针15.使用‘swap技巧’去除多余的内存 vectorintn(100,0); //删除一部分元素只留下十个元素但是容器的容量还是100 n.erase(b.begin()10,n.end()); //为了去除多余的容量使用swap vectorint(n).swap(n); //通过vectorint(n)创建一个隐式对象这个对象的容量大小就是n实际包含元素的大小然后与n进行交换再将隐式对象析构掉。消除对于容量。 //swap调用拷贝构造函数一次赋值函数两次将两个对象指针进行调换。不仅容器的内容就交换了指针引用都被交换了string除外但是相对来说还是在原来的容器中。//使用swap使内存最小 vectorint().swap(n); string strdada; string ().swap(str);16.避免使用vector 因为vector不满足c标准c如果是包含对象T的容器则 T pc[0]是可以通过编译的。但是对于vector是不行的。因为vector在内部是一位一位存的而不是按自己存储因为没办法创建指向单个位的指针 可以用deque替代和bitset替代大小固定不能插入元素。 17理解相等和等价的区别 等价不一定是相等的相等肯定是等价的。 对于类的等价取决于operator 函数中的函数体。在关联容器中例如map,set这种顺序存储的容器需要定义一个比较类去确定它们的顺序。 struct CIstringCompare{ public: binary_functionstring,string,bool bool operator()(const stringlhs,const stringrhs )const{ return ciStringCompare(lhs,rhs);//忽略string 大小写的比较 } }; setstring,CIstringCompare)ciss;18.为包含指针的关联函数指定比较类型 setstring *ssp; ssp.insert(new string(Anteater)); ssp.insert(new string(Wombat)); ssp.insert(new string(Lemur)); ssp.insert(new string(Penguin)); for(setstring *::iterator it;it!ssp.end();it){ cout*itendl; //打印的是地址 而且不一定是按照字符串顺序打印的因为set是根据地址大小进行排序的 }//因此需要写一个比较类去进行比较 struct DereferenceLess{templatetypename,PtrTypebool operator()(PtrType lhs,PtrType rhs){return *lhsrhs;} } setstring,DereferenceLessssp;//这样就按照字符串顺序进行打印19.总让比较函数在相等的情况下返回false //例如给setint,less_equalint s; s.insert(10); s.insert(10); //这会导致容器中出现两个10违背了set的定义因为在比较两个10是否等价时采用 !(1010)!(1010) //false 不等价 所以导致存了两个10 20.切勿直接修改set 或者 multiset的键 所谓的键就是进行比较等价的内容因为set是按顺序存储的所以不能直接修改。而map是修改不了因为map存储类型是pairconst k,v。 //但是有的sTL不支持修改set 它返回迭代器类型为 const T,想要修改就要类型转化,但是存在风险 //const_cast 指向原来的引用 const_castemployee(*it).setTitle(dasd); //使用static_cast则不行它会生成一个临时隐匿对象修改的是这个对象 static_castemployee(*it).setTitle(dasd); //相当于 (employee(it)).setTitle(dasd);安全的方法就是拷贝一份元素将其修改然后删除原有的再将其添加。 21.考虑用排序vector代替关联容器 当你的数据类型满足三个阶段时可以考虑用排序vector代替关联容器 插入删除阶段几乎不查询查找阶段几乎不查找重组阶段将元素全部删除再添加。 因为关联容器底层为平衡二叉树一个节点保活root,left,right三个地址相比vector太空间。 22.当效率至关重要谨慎选择opertor[] 和insert mapint,widgetm //在插入时使用operator[]相当于先要构造临时对象然后析构然后赋值 m[1]1.50; //相当于 typedef mapint,widgetIntWidgetMap; pairIntWidgetMap::iterator,boolresultm.insert(IntWidgetMap::value_type(1,widget())); result.first-second1.5; //不如使用insert m.insert(IntWidgetMap::value_type(1,50);//当更新数据时 m[1]51; //用insert需要构造然后析构然后赋值 m.insert(IntWidgetMap::value_type(k,v).first-secondv;当插入时用insert效率高当更新时用operator[]效率高。 23.使用distance 和advance将容器的const_iterator转换为iterator 当有的容器类的成员函数仅接受iterator作为参数,const_iterator不能作为他们都参数。必须进行强制类型转换。 typedef dequeint IntDeque; typedef IntDeque:: iterator Iter; typedef IntDeque:: const_iterator ConstIter; ConstIter ci; Iter i(ci); Iter i(const_castIter(ci));//const_iterator不能强制转为iterator //iterator 与const_iterator是两个不同的类它们之间的差距可能比string 与 double都远 //但是对于vector 与string是可以通过的因为它们的迭代器底层是char//使用advance 与distance实现安全转换 将ci 从const_iterator 转为iterator Iter i(d.begin()); advance(i,distanceConstIter(i,ci));//要加ConstIter 进行强制类型转换24.正确理解由reverse_iterator的base()成员函数所产生的iterator的用法 vectorintv; v.reserve(5); for(int i1;i5;i){v.push_back(i); } vectorint::reverse_iterator rifind(v.rbegin(),v.rend(),3); vectorint::iterator i(ri.base());执行完上述代码之后该vector和相应迭代器的状态如下图所示 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GGivqON4-1692172443245)(C:\Users\18440\AppData\Roaming\Typora\typora-user-images\image-20220915221408689.png)] 如果在reverse_iterator ri指向的位置插入新元素只需要在ri.base()位置处插入元素就行了。 但是reverse_iterator处删除元素的话则需要执行v.((ri).base()) 25.对于逐字符的输入请考虑使用istreambuf_iterator //将一个文本文件拷贝到string中 ifstream inputFile(interestingData.txt); //这段代码没有将空白字符加载进去因为istream_iterator使用operator函数完成实际的读操作默认operator函数会跳过空白字符 //如果没有消除skipws标志将默认忽略空白字符 inputFile.unsetf(ios::skipws);//消除sKipws标志位 string fileData((istream_iteratorchar(inputFile)),istream_iteratorchar());//但是拷贝速度不快因为每一个operator操作 需要执行许多额外的操作 //推荐使用istreambuf_iterator速度快40%它是直接从缓冲区中读取下一个字符 ifstream inputFile(intersetingData.txt); string fileData((istreambuf_iteratorchar(inputFile)),istreambuf_iteratorchar());#includeiostream #includerandom #includectime #includefstream #includeiterator using namespace std;int main(){ofstream outfile;outfile.open(file.txt,ios::app);srand(time(nullptr));for(int i0;i1000000;i){outfilerand()%26a;}clock_t start,stop;ifstream inputFile(file.txt);inputFile.unsetf(ios::skipws);startclock();string fileData1((istream_iteratorchar(inputFile)),istream_iteratorchar());stopclock();coutistream花费了(stop-start)endl;startclock();string fileData2((istreambuf_iteratorchar(inputFile)),istreambuf_iteratorchar());stopclock();coutistreambuf花费了(stop-start)endl;return 0; }[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6UDM6W5Q-1692172443246)(C:\Users\18440\AppData\Roaming\Typora\typora-user-images\image-20220916223026949.png)] 26.确保目标区间足够大 //给一个容器后面插入元素 int transmogrify(int x);//该函数根据x生成一个新值 vectorint values; …//给values 里面插入新值 vectorintresults; //给results.end()插入元素是错误的因为*resultes.end()里面没有元素 transform(values.begin(),values.end(),results.end(),transmogrify); //应该用back_inserter生成迭代器进行插入返回的迭代器将被push_back调用 transform(values.begin(),values.end(),back_inserter(results),transmogrify); //插在前面,被front_back调用 没有front_back的容器不能使用比如vector transform(values.begin(),values.end(),front_inserter(results),transmogrify); //如果担心需要重新分配空间就需要使用 reserver vectorintresults; results.reverse(results.size()values.size()); transform(values.begin(),values.end(),front_inserter(results),transmogrify);27.了解各种和排序有关的选择 如果需要对vector,string,deque或者数组中的元素执行一次完全排序那么可以使用sort或者stable_sort进行如果有一个vector,string,deque或者数组并且只需要对等价性最前面的n个元素进行排序那么可以使用partial_sort //将质量最好的前n个放在widgets的最前面 partial_sort(widgets.begin(),widgets.begin()n,widgets.end(),quealityCompare);如果有一个vector,string,deque或者数组并且只需要只需要找个第n个位置的元素或者需要找到等价性最前面的n个元素但有不对这n个元素进行排序那么可以使用nth_element //找出质量最好的前n个元素不用对n个元素进行排序 nth_element(widgets.begin(),widgets.begin()n-1,widgets.end(),quealityCompare); //找到质量中间的元素 vectorwidget::iterator itnth_element(widgets.begin(),widgets.begin()widgets.size()/2,widgets.end(),quealityCompare);如果需要将一个标准序列容器的元素按照是否满足某个特定的条件区分开那么partition和stable_partition很合适 //找到第一个质量比二级还差元素的位置 vectorwidget::iterator goodEndpartition(widgets.begin(),widgets.end(),hasAcceptableQulity); //返回第一个不满足大于二级质量条件的widget如果你的数据在一个list中仍然可以使用partition和stable_partition 排序算法的效率排序
partition,stable_partition,nth_element,partial_sort,sort,stable_sort. 28.如果确实需要删除元素则需要在remove这一类算法之后调用erase //remove的参数是迭代器并不知道容器的类型无法调用容器的成员函数 earse,所以用remove 容器中元素的数目并不会减少 templateclass ForwardIterator,class T ForwardIterator remove(ForwardIterator first,ForwardIterator last,const Tvalue); //remove只是把不用删除的元素移到区间的前部它返回迭代器指向最后一个“不用删除”的元素之后的元素。 remove(v.begin(),v.end(),99);事实上并没有将99的放在后面后面还保留着旧值用保留的值覆盖掉要删除的值 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OVd8Qqaz-1692172443246)(C:\Users\18440\AppData\Roaming\Typora\typora-user-images\image-20220917190200302.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FpdnjEKB-1692172443246)(C:\Users\18440\AppData\Roaming\Typora\typora-user-images\image-20220917190127261.png)] //为了真的删除空间中值使用erase v.erase(remove(v.begin(),v.end(),99),v.end()); //list的remove是和erase融合的 listintli; li.remove(99);remove对于包含指针的容器再用在和erase进行连用时记得先要释放内存防止内存泄漏。对于智能指针不用这样做。 29.使用accumulate进行区间统计 //统计list中所有值的和 listdouble ld; …//添加元素 double sumaccumulate(ld.begin(),id.end(),0.0); //统计容器中所有字符串长度之和 string::size_type StringLengthSum(string::size_type sumSoFar,const string s){return sumFoFars.size(); } setstringss; string::size_type lengthSumaccumlate(ss.begin(),ss.end(),static_caststring::size_type(0),stringLengthSum);//统计区间内所有点的平均值 struct Point{Point(double initX,double initY):x(initX),y(initY){}double x,y; } class PointAverage:public binary_functionPoint,Point,Point{public:PointAverage():xSum(0),ySum(0),numPoints(0){}const Point operator()(const Point avgSoFar,const Point p){numPoints;xSump.x;ySUmp.y;return Point(xSum/numPoints,ySum/numPoints);}private:size_t numPoints;double xSum;double ySum; } Point avgacculate(lp.begin(),lp.end(),Point(0,0),PointAverage());30.遵循按值传递的原则来设计函数子类 对于多态的函数对象不能使用虚函数因为参数类型是基类而实参类型是派生类的在传递的过程中会产生剥离问题在对象拷贝的过程中派生部分可能会被去掉而仅保留基类部分。 函数对象在STL中作为参数或者返回时总是值传递意味着第一函数对象小巧第二函数对象是单态。 31.确保判别式是纯函数 判别式是一个返回值为bool的函数。在STL中判别式有着广泛的用途。标准关联容器的比较函数就是判别式对于find_if以及各种排序算法判别式往往也被作为参数来传递。纯函数是指返回值仅仅依赖其参数的函数。判别类是一个函数的子类它的operator()函数是一个判别式。 判别式是纯函数的理由STL传递是按值传递所以如果依赖判别式依赖外界参数可能会导致出错。 32.若一个类是函数子应该让它可配接 listwidgetwidgetPtrs;//一个包含Widget对象指针的list容器 bool isInteresting(const Widget pw);//判断某个Widget指针所指对象是否足够有趣 //找第一个有趣的widget listWidget *::iterator ifind_if(widgetPtrs.begin().widgetPtrs.end(),isInteresting); //如果想第一个不有趣的widget listWidget *::iterator ifind_if(widgetPtrs.begin().widgetPtrs.end(),not1(isInteresting);//错误 listWidget *::iterator ifind_if(widgetPtrs.begin().widgetPtrs.end(),not1(ptr_fun(isInteresting));//正确//not1是函数配接器ptr_fun的作用是完成类型定义 //只有提供必要类型定义的函数对象被称为可被配接的函数对象 //提供这些类型定义最简单的方式就是让函数子从特定的基类继承。 //函数子operator只有一个参数就从std::unary_function继承 //函数子两个参数就从std::binary_function继承 //unary_function和binary_function是模板不能直接继承需要提供类型实参//operator一个参数继承std::unary_function templatetypename T //提供operator第一个参数的类型和返回值类型 class MeetsThreshold:public std::unary_functionwidget,bool{ private:const T threshold; public:bool operator()(const Widget) const; }//operator两个参数的继承 //当函数子不需要任何私有成员 //提供operator第一个参数的类型,第二个参数的类型和返回值类型 //operator是带引用和const而模板没有传递给unary_function,binary_function非指针类型需要去掉const 和 struct WidgetNameComapre:public std::binary_functionWidget,Widget,bool{bool operator()(const Widgets lhs,const widgetrhs)const; } //对于指针类型 则是可以 struct WidgetNameComapre:public std::binary_functionconst Widget ,const Widget,bool{bool operator()(const Widgets* lhs,const widget* rhs)const; }//类型定义后的子类可以直接只有函数配接器 listwidget::reverse_iterator ifind_if(widgets.begin(),widgets.end(),not1(MeetsThresholdint()))s33.理解ptr_fun,mem_fun和mem_fun_ref的由来 f(x);//f是一个非成员函数 x.f();//f是成员函数并且x是一个对象或者对象的引用 p-f();//f是成员函数并且p是一个指向对象x的指针 void test(widget w); vectorwidgetvw; for_each(vw.begin(),vw.end(),test);//可以通过编译 class widget{public:…void test();… } for_each(vw.begin(),vm.end(),widget::test);//不能通过编译 listwidget *lpw; for_each(lpw.begin(),plw.end(),widget::test);//不能通过编译 //原因 for_each算法是基于非成员函数实现的 //for_each算法的实现 templatetypename InputIterator ,typename Function Function for_each(InputIterator begin,InputIterator end,Function f){while(begin!end)f(*begin); } //mem_fun,mem_fun_ref用来调整使能够被成员函数使用 //mem_fun的定义 templatetypename R,typename C mem_funR,C;//C是类R是指向成员函数的返回类型 mem_fun(R(C::*pmf)()); //mem_fun带一个指向某个成员函数的指针参数pmf并且返回一个mem_fun_t类型的对象。mem_fun_t是一个函数子类它拥有该成员函数的指针并提供operator函数在operator函数中调用通过参数传递进来的对象上的成员函数 for_each(lpw.begin(),lpw.end(),mem_fun(widget::test));//可以通过编译34.算法调用优于手写的循环 效率算法通常比程序员自己写的循环效率高正确性自己写的循环比使用算法更容易出错可维护性使用算法的代码通常比手写循环的代码更加简洁明了。 如果要做的工作有算法实现就用算法。但是如果使用循环很简单而使用算法实现的话却要求使用绑定器和配接器或者要求一个单独的函数子类就使用循环。 35.容器的成员函数优于同名算法 成员函数往往速度很快成员函数通常和容器结合的更加紧密 setints; … //插入一百万个值 setint::iterator is.find(727);//以对数时间运行 if(i!s.end())… setint::iterator ifind(s.begin(),s.end(),727);//使用find算法 以线性时间运行 35.正确区分count,find,binary_search,lower_bound,upper_bound和equal_range 对于区间没有排序的应当使用count和find它们是线性时间使用相等性查找 vectorints;count是找区间中存在某个特定值的个数 int numcount(s.begin,s.end(),5);//查找5在区间存在的个数find 是找区间中第一个特定值的位置 int indexcount(s.begin,s.end(),5);//查找第一个5的位置对于区间排序的可以使用binary_search,equal_range,lower_bound对数时间,使用等价性查找 vectorwidgetvm; … wdiget w;binary_search只回答是否存在的问题 bool existbinary_search(vm.begin(),vm.end(),w);//查看5是否存在lower_bound返回一个迭代器如果存在返回第一份的拷贝如果不存在返回适合于插入该值的位置 vectorwidget::iterator itlower_bound(vm.begin(),vm.end(),w);equal_range,返回一对迭代器第一个迭代器等于lower_bound返回的迭代器第二个迭代器等于upper_bound指向区间内最后一个等价元素的下一个位置返回的迭代器 typedef vectorwidget::iterator VMIter; typedef pareVMIter,VMIter VMIterPair; VMIterPair pequal_range(vm.begin(),vm.end(),w); //两个迭代器之间的距离即使原始区间查找等价对象的数目 if(p.first!p.second){ //说明存在 } { //说明不存在两个迭代器都指向w的插入位置 }[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TvGCesVE-1692172443247)(C:\Users\18440\AppData\Roaming\Typora\typora-user-images\image-20220918171324230.png)]
36.避免直写型的代码 假如有一个vector现在想删除其中所有其值小于x的元素但是在最后一个其值不小于y的元素之前的所有元素应该保留下来。 vectorint v; int x,y; … v.erase(remove_if(v.rbegin(),v.rend(),bind2nd(greater_equalint(),y)).base(),v.end(),bind2ndlessint(),x)),v.end()); //可读性太差,不利于后来者维护 //进行分解 typedef vectorint::iterator VecIntIter; //初始化rangeBegin,使它指向v中大于等于y的最后一个元素之后的元素 //如果不存在这样的元素则rangeBegin被初始化为v.bgein() //如果这样的元素正好是V最后一个元素则rangeBegin被初始化为v.end() VecIntIter rangeBeginfind_if(v.rbegin(),v.rend(),bind2nd(greater_equalint(),y)).base(); //从rangeBegin到v.end()的区间中删除所有小于x的值 v.erase(remove_if(rangeBegin,v.end(),bin2nd(lessint(),x)),v.end()); //bind2nd 将二元函数转为一元函数因为函数内一个参数已经被确定37.总是包含#include正确的头文件 因为不同的平台头文件之间包含关系不通因此任何时间如果你使用某个头文件中的一个STL组件一定要提供对应的#include 指令 [外链图片转存中…(img-TvGCesVE-1692172443247)] 36.避免直写型的代码 假如有一个vector现在想删除其中所有其值小于x的元素但是在最后一个其值不小于y的元素之前的所有元素应该保留下来。 vectorint v; int x,y; … v.erase(remove_if(v.rbegin(),v.rend(),bind2nd(greater_equalint(),y)).base(),v.end(),bind2ndlessint(),x)),v.end()); //可读性太差,不利于后来者维护 //进行分解 typedef vectorint::iterator VecIntIter; //初始化rangeBegin,使它指向v中大于等于y的最后一个元素之后的元素 //如果不存在这样的元素则rangeBegin被初始化为v.bgein() //如果这样的元素正好是V最后一个元素则rangeBegin被初始化为v.end() VecIntIter rangeBeginfind_if(v.rbegin(),v.rend(),bind2nd(greater_equalint(),y)).base(); //从rangeBegin到v.end()的区间中删除所有小于x的值 v.erase(remove_if(rangeBegin,v.end(),bin2nd(lessint(),x)),v.end()); //bind2nd 将二元函数转为一元函数因为函数内一个参数已经被确定37.总是包含#include正确的头文件 因为不同的平台头文件之间包含关系不通因此任何时间如果你使用某个头文件中的一个STL组件一定要提供对应的#include 指令 在一个声明为const的成员函数内部该类所有的非静态数据成员都自动被转换为相应的const类型。