C++泛型笔记

本文深入探讨了C++泛型算法及其在容器操作中的应用,包括类型别名、构造函数、赋值与swap、容器大小、元素添加与删除等关键概念。详细解释了迭代器类型、关系运算符、顺序容器访问方式、泛型算法如accumulate、equal、fill等,并通过实例展示了如何使用这些工具提高代码效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++泛型笔记

容器操作

类型别名

————
iterator此容器类型的迭代器类型
const_iterator可以读取元素但不能修改元素的迭代器类型
size_type无符号整数类型,足够保存此种容器类型最大可能容器的大小
difference_type带符号整数类型足够保存两个迭代器之间的距离
vlaue_type元素类型
reference元素左值类型;与vlaue_type&含义相同
constreference元素的const左值类型(即,const value_type&

构造函数

————
C c;默认构造函数,构造空容器(arrayP301)
C c1(c2);构造c2的拷贝c1,c1初始化为c2的拷贝。c1c2必须是相同的类型
(即它们必须是相同的容器类型,且保存的是相同的元素类型;
对于arary类型两者还必须具有相同的大小)
C c(b,e);构造c,将迭代器be指定的范围内的元素拷贝到c(array不支持)
范围内的元素类型必须与C的元素类型相容,而容器类型不必相同
C c{a,b,c }
C = {a,b,c }
列表初始化c,列表中的类型必须与C中的元素类型相容
(即列表中的元素类型可以转化为容器中的元素类型)

只有顺序容器(不包括array
的构造函数才能接受大小参数

C seq(n)|seq包括n个元素,这些元素进行了值初始化;
此构造函数是explicit的(string不适用)
C seq(n,t)|seq包含n个初始化为值t的元素

赋值与swap

————
c1=c2c1中的元素替换为c2中的元素,c1c2必须有相同的类型
c1 = {a,b,c }c1中的元素替换为列表中的元素(不适用于array
a.swap(b)交换ab的元素,c1c2必须有相同的类型,大小不必相同
swap(a,b)a.swap(b)等价

assign不适用于关联容器和array
seq.assign(il)|将seq中的元素替换为初始化列表il中的元素
seq.assign(n,t)|将seq中的元素替换为n个值为t的元素

注意

  • assign相当于先对容器进行clear操作,然后再对容器进行添加元素的操作
  • assign相关操作会导致左边容器内部的迭代器、引用和指针失效。
  • swap交换两个具有相同类型的容器的内容,除array外,swap只是交换了两个容器的数据结构,元素本身并未交换,而且指向容器的迭代器、指针、引用在`swap后不会失效。它们仍指向swap操作之前所指向的那些元素。但是,在swap之后,这些元素已经属于不同的容器。
  • swap array会真正交换它们的元素,也就是进行了复制操作,所以原来的指针、引用和迭代器绑定的元素保持不变。但是元素值已经和另一个array中对应元素的值进行了交换。
  • string来说,调用swap会导致迭代器、引用和指针失效。

容器大小

————
c.size()c中元素的数目(不支持forward_list
c.max_size()c可保存的最大元素数目
c.empty()c中存储了元素,返回false,否则返回true

添加/删除元素(不适用与array)

————
c.insert(args)args中的元素拷贝进c
c.emplace(inits)使用inits构造c中的一个元素
c.erase(args)删除args指定的元素
c.clear()删除c中的所有元素,返回void

注:不同容器中这些操作的接口都不同

向顺序容器中添加元素

————
c.push_back(t)
c.emplace_back(args)
c的尾部创建一个值为t或由args创建的元素。返回void
c.push_front(t)
c.emplace_front(args)
c的头部创建一个值为t或由args创建的元素。返回void
c.insert(p,t)
c.emplace(p,args)
在迭代器p指向的元素之前创建一个值为t或由args创建的元素。
返回指向新添加元素的迭代器
c.insert(p,n,t)在迭代器p指向的元素之前插入n个值为t的元素。
返回指向新添加的第一个元素的迭代器;若n为0,则返回p
c.insert(p,b,e)将迭代器be指定的范围内的元素插入到迭代器p指向的元素之前。be
不能指向c中的元素。返回指向新添加的第一个元素的迭代器;若范围为空,则返回p
c.insert(p,il)il是一个花括号包围的元素值列表。将这些给定值插入到迭代器p指向的元素之前。
返回指向新添加的第一个元素的迭代器;若范围为空,则返回p

顺序容器的删除操作

————
c.pop_back()删除c中的尾元素。若c为空,则函数行为未定义。函数返回void
c.pop_front()删除c中的首元素。若c为空,则函数行为未定义。函数返回void
c.erase(p)删除迭代器p所指的元素,返回一个指向被删元素之后元素的迭代器,若p指向尾元素,
则返回尾后(off-the-end)迭代器。若p是尾后迭代器,则函数行为未定义
c.earse(b,e)删除迭代器be所指定范围内的元素。返回一个指向最后一个被删元素之后元素的迭代器,
e本身就是尾后迭代器,则函数也返回尾后迭代器。
c.clear()删除c中的所有元素。返回void

注意:

  • 以上顺序容器的添加和删除操作都会改变容器的大小,所以不适用与array
  • forward_list有自己专有版本的insertemplaceerase(P312),forward_list不支持push_backempalce_backpop_back
  • vectorstring不支持push_frontemplace_frontpop_front
  • 向一个vectorstring插入元素,会使所有指向容器插入位置后的迭代器、引用、指针失效,删除类似,删除点之后位置的迭代器、引用和指针都会失效。;deque中删除或者插入除首尾位置之外的任何元素都会使所有迭代器、引用和指针失效。对于listforward_list,指向容器的迭代器(包括尾后迭代器和首前迭代器)、指针和引用仍有效。
    所以我们一般保存尾后迭代器:
auto begin = v.begin(),end = v.end();
while(begin != end){ //由于insert使得end的值已经改变
    ++begin;
    begin = v.insert(begin,42); //begin后的迭代器都会失效,包括尾后迭代器
    ++begin;
}
  • 虽然vectorstring都不支持push_front操作,但是他们对于insert操作并无类似限制(插入开始位置),但是这很耗时。
vector<string> svec;
svec.insert(svec.begin(),"Hello!")
  • 使用emplace操作
//在c的末尾构造一个Sales_data,调用了Sales_data对应3个参数的构造函数
c.emplace_back("987-0590",25,15.99);

//错误:没有接受3个参数的push_back版本
c.push_back("978-0590",25,15.99);

//正确:创建一个临时的Scales_data对象传递给push_back,但是相比2来说,3多调用了一次复制构造函数
c.push_back(Sales_data("978-0590",25,15.99));

emplaceemplace_backemplace_front都适用于向容器中添加自定义的对象。

  • emplaceinsert都是在迭代器指向元素之前插入或创建元素,返回最新添加的元素,以便下一次在新添加的元素前继续添加。而erase是删除迭代器指定元素或范围内的元素,返回最后一个被删除元素之后元素的迭代器。以便下一次继续删除。

顺序容器中访问元素操作

————
c.back()返回c中尾元素的引用。若c为空,函数行为未定义
c.front()返回c中首元素的引用。若c为空,函数行为未定义
c.[n]返回c中下标为n的元素的引用,n是一个无符号整数。若n>c.size(),则函数行为未定义
c.at()返回c中下标为n的元素的引用,如果下标越界,则抛出一out_of_range异常
  • 对空容器中元素访问的行为(通过迭代器或者上面的方式),就相当于下标越界,所以访问前要用empty验证容器是否为空。
  • c.back()c.front()c.[n]返回的都是引用,若容器是const的返回引用也是const,若容器不是const,那么返回的是普通引用,可以通过它改变容器中元素的值。如:
c.front() = 42;

关系运算符

————
==,!=所有容器都支持相等(不等)运算符
<,<=,>,>=关系运算符(无序关联容器不支持)

获取迭代器

————
c.begin(),c.end()返回指向c的首元素和尾元素之后位置的迭代器
c.cbegin(),c.cend()返回const_iterator

反向容器的额外成员(不支持forward_list

————
reverse_iterator按逆序寻址元素的迭代器
const_reverse_iterator不能修改元素的逆序迭代器
c.rbegin(),c.rend()返回指向c的尾元素和首元素之前位置的迭代器
c.crbegin(),c.crend()返回const_reverse_iterator

迭代器范围

它由一对迭代器beginend表示,必须满足以下条件:
+ 它们指向同一容器中的元素,或者是最后一个元素之后的位置。
+ 我们可以通过反复递增begin来达到end。换句话说,end不能在begin之前

注意:迭代器范围是一个左闭右开的区间

泛型算法

accumulate

定义在头文件numeric
入口参数:
accumulate(begin,end,init)
beginend表示一个迭代器范围,init表示累加的初始值,容器中元素类型必须支持+运算符

equal

入口参数:
equal(v1.begin(),v1.end(),v2.begin())
v1.begin()v1.end()表示容器v1的迭代范围,v2.begin()表示从容器v2开始进行比较,v2的长度必须大于v1的长度,容器类型以及容器中元素类型都不必一样。

fill

入口参数:
fill(begin,end,value)
beginend表示一个迭代器范围,value表示写入序列的数据的值

fill(v.begin(),v.end(),0);
fill(v.begin(),v.begin()+v.size()/2,10);

fill_n

fill(begin,num,value)
begin是迭代器,表示开始写入元素的位置,num表示写入元素的个数,value表示写入的值。从begin开始至少有num个元素。

vector<int> v;
fill_n(v.begin(),v.size(),0); //所有元素重置为0

vector<int> v1; //空向量
fill_n(v1.begin(),10,0); // v1中并没有这么大空间,造成越界

插入迭代器

是一种迭代器适配器,接受一个容器,生成一个迭代器。该迭代器调用容器操作来向给定容器的指定位置插入一个元素。

插入迭代器的操作
————
it = t在指定的当前位置插入值t。假定cit绑定的容器,依赖于插入迭代器的不同种类,此赋值分别
会调用c.push_back(t)c.push_front(t)c.insert(t,p),其中p为传递给inserter的迭代器位置。
插入迭代器的种类
————
back_inserter创建一个使用push_back的迭代器
front_inserter创建一个使用push_front的迭代器
inserter创建一个使用inserter的迭代器。此函数接受第二个参数,
这个参数必须是一个指向给定容器的迭代器。元素将被插入到给定迭代器所表示的元素之前。

注意:

  • 只有在容器支持push_backpush_front的情况下,才可能使用back_inserterfront_inserter
  • front_inserterinserter的区别
// it是由inserter生成的迭代器
*it = val;
// 与下面代码等价
it = c.insert(it,val); // it指向新加入的元素
++it; //递增it,使它指向原来的元素
list<int> lst = {1,2,3,4};
list<int> lst2,lst3; // 空list
// 拷贝完后,lst2包含 4,3,2,1,是逆序的
copy(lst.cbegin(),lst.cend(),front_inserter(lst2)); // 由于front_inserter返回的迭代器,每次插入后,
                                                    //迭代器都指向首元素,而不是递增,指向原来的元素
// 拷贝完后,lst2包含 1,2,3,4
copy(lst.cbegin(),lst.cend(),inserter(lst3,lst3.begin()));

vecot<int> v; //空向量

//通过back_insert返回的迭代器执行的是插入操作,不用考虑越界问题,而且每次插入元素后,
//迭代器会自动递增,指向新加入的元素,而且插入的顺序是正序的,这与inserter是相同的。
fill_n(back_insert(v),10,0);        //如果第一个参数为v,会出现越界问题
  • 插入器常常与泛型算法,如:fill_ncopy,联用,避免for循环,代码更加简洁。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值