12. vector
12.2 存储
vector使用堆内存存储元素,即vector中的元素存储于堆
12.1 vector()
函数:vector(size_type n, const value_type &val = value_type())
功能:构造函数,初始包含「n」个元素,每个元素的值为「val」
说明:
①「val」带有默认参数「value_type()」,使用的是值初始化,因此对于没有指定初值「val」的内置类型的vector来讲,元素将被妥善地初始化(初始化为0)
② 对于vector,可以通过该函数预订空间,而无需显示地调用resize()函数
示例:
11. string
11.3 erase()
函数:
string& erase(size_t pos, size_t len)
iterator erase(iterator p)
iterator erase(iterator first, iterator last)
功能:删除指定的元素
删除起始索引为「pos」,长度为「len」的元素,若元素个数不足「len」,则取最大值;
删除p指向的元素;
删除[first, last)范围内的元素;
11.2 rbegin()
函数:reverse_iterator rbegin() [const]
返回值:返回指向string最后一个字符的反向迭代器
示例:
11.1 compare()
函数:int compare(size_t pos, size_t len, const std::string &str) [const]
功能:把string对象中起始索引为「pos」,长度为「len」的子串,与str进行比较
返回值:
= 0相等;
< 0对于第一个不相等的字符,string中的对应字符 < str中的对应字符;所有参与比较的字符都相等,但在「len」的范围内,string子串的长度 < str的长度;
> 0对于第一个不相等的字符,string中的对应字符 > str中的对应字符;所有参与比较的字符都相等,但在「len」的范围内,string子串的长度 > str的长度;
说明:
① 如果string从索引「pos」开始的长度,小于「len」,则取最大子串;
② 通过src.compare(pos, obj.len, obj),可以进行安全的字符串比较,不必关心超界问题;
示例:
10. list
说明:
能够调用list的push函数,如「push_front」,「push_back」的前提是:类型T具有拷贝构造函数,且为public
9. map
lower_bound()
函数原型:iterator lower_bound(const key_type &k);
功能:返回key大于等于k的第一个元素
upper_bound()
函数原型:iterator upper_bound(const key_type &k);
功能:返回key大于k的第一个元素
8. map限制
const map不能调用operator[],即下面非法:
cmp[k];
其中cmp为任意类型的const map<K, V>
能够调用map下标运算符的前提是:mapped_type具有默认构造函数,且为public
示例:
std::map<K, V> mp;
mp[k];
类型V必须具有默认构造函数
7. map和set
7.1 迭代器类型
双向迭代器,Bidirectional迭代器,可前进可后退,一次一步。
iterator it;
it+= n; 或 it -= n; //n等于1
std::advance(it, 3); ++it调用三次,实现前进三步。
7.2 内置类型
std::map<key_type, mapped_type> mp;
std::set<key_type> st;
map的value_type:std::pair<key_type, mapped_type>
set的value_type:key_type
7.3 判断元素存在
标准形式:
函数原型:size_t count(const key_type &k) const
功能:返回容器中键值等于k的元素个数。
返回值:对于set和map,键值唯一,故存在即1、否则为0;对于multiset和multimap,存在多键值,返回值>=0。
7.4 插入元素
标准形式:
函数原型:std::pair<iterator, bool> insert(const value_type &val);
功能:插入元素。
返回值:std::pair<iterator, bool>。bool为false表失败,键值已存在,否则true表成功;iterator为指向新插入元素的位置,或返回已存在的同值元素的位置。
7.5 map取值
标准形式:
7.6 删除元素
标准形式:
函数原型:size_t erase(const key_type &k);
功能:删除键值等于k的元素。
返回值:被删除的元素个数。对于map和set,键值唯一,故返回值非0即1;对于multimap和multiset,存在多键值,返回值>=0。
6. 空间分配和预定
reserve(size_t n):预定空间,增大capacity,避免容器增长导致重新分配内存;若改小,不执行
resize(size_t n, value_type v):调整实际大小,使容器内元素个数等于n
n小于元素个数——截断,只保留前n个元素,其余删
n大于元素个数——填,从最后插入新元素,直到元素个数达到n;新元素为v的副本,若无参数v,则使用value_type的默认值或默认构造函数构造新 元素
差别
结果
小评:std::string s; s.resize(size_t n, char c); 等价于 std::string s(size_t n, char c);
5.map和set遍历过程中不可以任何方式删除当前迭代器所指元素
std::map<UINT, std::string> map;
map.insert(std::make_pair(1, "a"));
map.insert(std::make_pair(2, "b"));
map.insert(std::make_pair(3, "c"));
map.insert(std::make_pair(4, "d"));
map.insert(std::make_pair(5, "e"));
for(auto it = map.begin(); it != map.end(); ++it)
{
printf("%-10d%-10s\n", it->first, it->second.c_str());
if(it->second == "c")
{
UINT cid = it->first;
map.erase(cid);
}
}
结果
4. multimap查询标准格式
bool IsExist(const std::string &sUsername, const UINT cid)
{
auto beg = mmap.lower_bound(sUsername);
auto end = mmap.upper_bound(sUsername);
for(; beg != end; ++beg)
{
if(beg->second == cid)
return true;
}
return false;
}
3.map添加方式的选择
typedef struct MapStruct
{
MapStruct()
{
printf("Default Constructor\n");
}
MapStruct(const int i) : iCount(i)
{
printf("Simple Constructor\n");
}
MapStruct(const MapStruct &rhs) : iCount(rhs.iCount)
{
printf("Copy Constructor\n");
printf("lhs=%p\nrhs=%p\n", this, &rhs);
}
MapStruct(MapStruct &&rhs) : iCount(std::move(rhs.iCount))
{
printf("Move Constructor\n");
printf("lhs=%p\nrhs=%p\n", this, &rhs);
}
MapStruct& operator=(const MapStruct &rhs)
{
std::cout<<"Copy Assignmen Operator"<<std::endl;
iCount = rhs.iCount;
return *this;
}
MapStruct& operator=(MapStruct &&rhs)
{
std::cout<<"Move Assignmen Operator"<<std::endl;
iCount = std::move(rhs.iCount);
rhs.iCount = 0;
return *this;
}
int iCount;
}MapStruct;
3.1 map[key] = value
int main()
{
std::map<int, MapStruct> map;
map[2] = MapStruct(5);
}
结果:解释:
执行过程
1. MapStruct(5),调用普通构造函数,生成=右侧临时对象
2. MapStruct(),调用默认构造函数,生成临时对象,地址0012E2D0
3. std::pair<int, MapStruct>(2,0012E2D0),因2和0012E2D0均属右值,故调用std::pair的构造函数std::pair<T1, T2> std::pair(T1&&, T2&&),生成std::pair临时对象。此构造函数原型:
pair(_Ty1x&& _Val1, _Ty2x&& _Val2)
: _Mybase(_STD move(_Val1),
_STD move(_Val2))
{}
最终调用
_Pair_base(_Ty1x&& _Val1, _Ty2x&& _Val2)
: first(_STD move(_Val1)),
second(_STD move(_Val2))
{}
完成pair的构造,first和second为_Pair_base唯一两个成员变量,如
_Ty1 first;// the first stored value
_Ty2 second; // the second stored value
此例中为
int first;
MapStruct second;
故调用MapStruct移动构造函数生成second,地址0012E2C4
4. 生成Node节点,Map为红黑树,以节点存储。Node中存在std::pair型成员,构造此成员时使用3中临时量进行拷贝初始化,临时量属右值,故调用移动构造函数,即
template<class _Other1,
class _Other2>
pair(pair<_Other1, _Other2>&& _Right)
: _Mybase(_STD forward<_Other1>(_Right.first),
_STD forward<_Other2>(_Right.second))
{}
最终调用
template<class _Other1,
class _Other2>
_Pair_base(_Other1&& _Val1, _Other2&& _Val2)
: first(_STD forward<_Other1>(_Val1)),
second(_STD forward<_Other2>(_Val2))
{}
完成pair的构造,经过复杂的类型传递,_Other1 = int, _Other2 = MapStruct。std::forward<T>(arg)函数返回绑定到arg的T&&,T&&适用引用折叠。故再次调用MapStruct的移动构造函数生成second,地址003E2CC8
5. 最后,由=右侧的临时量MapStruct(5),为003E2CC8赋值,临时量属右值,调用移动赋值运算符。
结论:
map以map[key] = value形式添加元素,假设map[key]不存在,则执行过程如下
1.生成右侧value
2.在map中添加key-默认value
3.右侧value给默认value赋值
附加:
标准库中类拷贝构造函数定义为
Type(const Type &)
移动构造函数定义为
Type(Type &&)
其中const Type &可以绑定到左值也可绑定到右值,故当Type存在移动构造函数且以右值进行拷贝初始化时,右值与Type&&精确匹配,即调用移动构造函数;当Type不存在移动构造函数时,const Type &将成为最佳匹配调用拷贝构造函数。赋值运算符情况类似。
综上,当一个类存在拷贝操作而不存在移动操作,所有的移动操作将调用对应的拷贝操作完成。而且除非显示定义为delete,每个类必有拷贝操作,若无,编译器自动合成,合成条件宽泛——即不存在。
3.2 map.insert(std::make_pair(key, value))
map.insert(std::make_pair(1, MapStruct(2)));
结果:
结论:
map以map.insert(std::make_pair(key, value))形式添加元素,与3.1相同,仅省去最后一步赋值操作。
3.3
MapStruct tmp(2);
mp.insert(std::make_pair(1, tmp));
结果:
结论:
与3.2相同,仅调用std::make_pair(1, tmp)时传入的参数为MapStruct左值,故在创建临时pair的second成员时执行的拷贝初始化,调用的是拷贝构造函数。
3.4
if(map[4].iCount == 5)
std::cout<<"map[4].iCount == 5"<<std::endl;
std::cout<<map.size()<<std::endl;
结果:
实验过程:
1.map[4]不存在
2.以map[4].iCount作为判断条件
3.输出"MapStruct Default Construction"
实验结论:
1.map[key]以任何形式存在,只要程序执行到,无论key是否存在,自动插入key-value
2.map[key].member绝对安全,即便key在map中不存在,只要执行到map[key]则自动添加
3.5
if(2==3 && map[5].iCount==3)
std::cout<<"Ok"<<std::endl;
std::cout<<map.size()<<std::endl;
结果:
实验说明:
1.map[5]不存在
2.2==3 && map[5].iCount==3作为if语句判断条件
实验结果:
1.MapStruct默认构造未被调用
2.key=5未自动添加到map
故map[5].iCount==3根本没有被执行
实验结论:
condition1 && condition2作为判断条件,若condition1==false直接以false返回,condition2根本不会被执行
3.6
if(2==2 || map[5].iCount==3)
std::cout<<"Ok"<<std::endl;
std::cout<<map.size()<<std::endl;
结果:
实验说明:
1.map[5]不存在
2.2==2 || map[5].iCount==3作为if语句判断条件
实验结果:
1.MapStruct默认构造未被调用
2.key=5没有自动添加到map
故map[5].iCount==3根本没有被执行
实验结论:
condition1 || condition2作为判断条件,若condition1==true直接以true返回,condition2不会被执行如同不存在
1. map 用来存储键值对,key-value的形式。
增删改查速度都很快,查找速度在log(N)。
map和set的插入删除效率比用其他顺序容器高。这是因为对于关联容器来说,不需要做内存拷贝和内存移动。
map和set容器内所有元素都是以节点的方式来存储,其节点结构和链表差不多。
整数作key会提高map效率。
1.1 插入
std::map<int,double> map;
map.insert(std::pair<int,double>(1, 24.3));
map.insert(std::pair<int,double>(2, 21.7));
1.2 遍历
std::map<int,double>::iterator iter;
for(iter = map.begin(); iter != map.end(); iter++)
{
int key = iter->first;
double value = iter->second;
std::cout<<key<<value<<std::endl;
}
1.3 查找 iterator find (const key_type& k);
//std::map<UINT, std::string> map
std::map<UINT, std::string>::iterator iter = map.find(14);
if(iter != map.end())
{
std::cout<<"存在"<<std::endl;
}
map 是关联容器,对于关联容器来说,如果某一个元素已经被删除,那么其对应的迭代器就失效了,不应该再被使用;否则会导致程序无定义的行为。
标准写法:删除之前,迭代器先定位到下一个元素。
for(ITER iter=mapTest.begin();iter!=mapTest.end();)
{
cout<<iter->first<<":"<<iter->second<<endl;
mapTest.erase(iter++);
}
1.4 删除
std::map<int, std::string> map;
map.erase(14);
程序运行时会给vector分配一片连续存储区,来适应vector的增长。当vector继续增大、存储区不足以容纳时,程序扩展连续存储区。
如果没有足够连续区域分配,则程序将在其他地方申请足够容量的存储区,然后将vector拷贝过去。
优点:支持随机访问,例如:vecNames.at(16)
2.1 添加
std::vector<std::string> vecNames;
vecNames.push_back("rongbaibai");
2.2 查找
std::vector<std::string> vec;
auto pos = std::find(vec.begin(), vec.end(), "fengsha");
if(pos == vec.end())
return false;
int iDst = std::distance(vec.begin(), pos);
2.3 空间预定
示例一
int iNum = GetResultNum();
if(iNum == 0)
return false;
std::vector<Data> vec(iNum);
bool bRet = GetResult(&vec[0]);
利用Data的默认构造函数生成一个大小为iNum的vector。
示例二
std::vector<Data> vec;
int iNum = GetResultNum();
if(iNum == 0)
return false;
vec.resize(iNum, data);
bool bRet = GetResult(&vec[0]);
将vector大小改为iNum,多出来的元素都是data的副本。