六、STL基础
1、一些只需要写一次的函数是非常适合用lambda函数(没有名字的函数)来实现的。
lambda可以作为local对象,也可以作为参数传入。lambda相当于是内联函数,效率高。
auto local = [](int a, int b){
std::cout << a << b;
};
local(1, 2);
template<typename Func>
void test(Func f, int a, int b){
f(a, b);
}
test([](int a, int b){
std::cout << a << b;
}, 1, 2);
int a = 1;
int b = 1;
auto local= [&](){
std::cout << a << b;
};
local();
2、forward_list不支持size()函数。
3、std::array是直接在栈上分配内存的。
std::array<int,5> a; // 里面的值是乱的
std::array<int,5> b = {}; // 里面的值是0
std::array<int,5> c = {1}; // 第一个值是1,其余的值是0
a.swap(b); // 线性的时间复杂度
swap(a,b); // 线性的时间复杂度
a[1]; // 不检查下标是否合法
a.at(1);// 检查下标是否合法,会抛异常.
a.data();// 得到数组的指针.
auto info = std::get<1>a; // tuple的形式,和a[1]是一样的意思.
a.fill(0); // 给所有的值赋值为0.
除array之外,其他容器的swap是非常高效的(只交换一些指针值).
4、std::vector里面不要存bool。因为,C++中,vector<bool>为了达到节省内存的目的,专门做了特化,大概方式就是用bit位来存储数组中的元素。代价就是,这个容器里面的内置类型乱掉了。
std::vector<bool> a;
a[0]; //类型并不是bool!!!
std::vector<float> a(10); // 10个元素,值为0.0
std::vector<float> b(10,1.0f); // 10个元素,值为1.0f
b.reserve(100); // 分配100个元素的空间
b.assign(5, 1.0f); // 5个元素,值为1.0f
b.emplace(b.end(), 1.0f);// C++11
b.emplace_back(1.0f); // C++11
b.resize(5); // 重新设置当前大小为5
b.resize(5, 1.0f); // 重新设置当前大小为5,如果当前元素个数不足5个,则新增的那些值为1.0f。如果当前元素大于等于5个,那1.0f这个参数就用不上了。
b.clear(); // 容量不会变少,内存还是那么大。要把内存降下来的话,调用shrink_to_fit()函数建议系统去降,请求容器降低其容量和size匹配。
b.shrink_to_fit(); //c++11
emplace操作与push、insert的区别:
emplace成员函数利用传入的参数在容器管理的内存中直接构造元素;push与insert成员函数则是将传入的元素类型对象拷贝到容器中,或创建一个局部临时对象,并将其压入容器中。
注意:emplace函数的参数根据元素类型而变化,参数必须与元素类型的构造函数相匹配。
std::vector<MyClass> a;
a.emplace_back(MyClass(1)); // C++11
因此,建议用emplace,而不建议用insert。
在服务器编程中,从客户端收到的包可以用vector来存放。
先进先出的模型,也就是先收到的包要先发出去,可以用deque来存放。
5、std::list插入和删除不会造成迭代器失效。
std::list<float> a;
a.remove(1.0f); // 删除所有值为1.0f的节点
a.remove_if([](auto v){ return v>100.0f; }); // v是list里面的元素的值.
a.reverse(); // 反转链表
a.sort(); // 从小到大排序.
std::list<float> b;
a.merge(b); // 合并两个排好序的链表
a.unique(); // 删掉排好序的链表中的重复的元素
a.splice(a.begin(), b); // 将链表b插入到a.begin的位置.
6、单向链表。
std::forward_list<float> a;
a.before_begin(); // 返回第一个元素的前一个位置
a.cbefore_begin();
a.insert_after(a.before_begin(), 10.0f); // 插入第一个元素.
a.erase_after(a.before_begin()); // 删除第一个元素.
// 在数字3前面插入一个元素
std::forward_list<int> a{1,2,3,4,5};
auto iter = a.before_begin();
for(int i=0; i<2; ++i) ++iter;
a.insert_after(iter, 10);
7、容器里面如果存放的是weaked_ptr的时候,遍历的时候效率很慢。
8、对于set可以用count函数来判断一个东西是否存在于容器中,但是对于multiset应该用find函数。
std::set<float> a = {1,2,4,5,6};
auto lower = a.lower_bound(5); // 返回5的位置
if(lower != a.end()){
if(*lower == 5){
// ...
}
}
auto lower1 = a.lower_bound(3); // 返回4的位置
auto upper1 = a.upper_bound(3); // 返回4的位置
auto lower2 = a.lower_bound(5); // 返回5的位置
auto upper2 = a.upper_bound(5); // 返回6的位置
auto er = a.equal_range(5); // 返回一个pair,其中first是lower_bound的结果,second是upper_bound的结果。
auto state = a.insert(100); // 返回一个pair<Iterator,bool>,插入成功与否看该pair的second。
自己实现set的第二个模板参数:
struct CompareAge{
template<typename T>
bool operator()(const T& t1, const T& t2) const{
return t1.age() < t2.age();
}
};
std::set<Person, CompareAge> a;
Person p;
a.find(p); // 是通过CompareAge去查找的.
std::find(a.begin(), a.end(), p); // 是通过操作符==去查找的.
// 查找map中的某个元素
std::map<int> a;
auto findIter = a.find(10);
if(findIter != a.end()){
...
}
9、如果对顺序没有需求的话,建议用unordered_map/unordered_set,而不建议用map/set。
要自定义MyClass的hash算法才能用unordered_map/unordered_set:
template<class T> inline void hash_combine(std::size_t &seed, const T &v) {
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
namespace std {
template<> struct hash<Position> { // 模板的特化
size_t operator()(const Position& p) const {
auto key = hash<int>()(p.x());
hash_combine(key, p.y());
return key;
}
};
}
std::unordered_set<Position> a;