建设银行贵金属网站/山西百度推广开户
一、容器与模板
前文就说到,标准库基于模板编程,定义了许多容器类以及一系列泛型算法,使程序员可以更简洁、抽象和有效地编写程序。C++标准库中有大量的标准容器,这些容器通常包含一组数据或对象的集合,几乎可以和任何类型的对象一起使用,C++开发者只需要为程序选择合适的容器即可。例如,STL库带给我们栈、自动增长的vector、map等等,就可以使开发者集中精力于业务应用,而不用重复制作轮子。了解所有容器,对于C++开发者来说至关重要。
想来大家对c/c++语言的一门课数据结构不陌生吧,它是c/c++编程密切相关的科目,几乎任何特定的数据结构都是为了实现某种算法而创建的。标准库中的容器,就是将运用最广泛的一些数据结构实现出来。常用的数据结构不外乎是数值、链表、树、堆栈、队列、散列表、集合、映射表等。容器的好用及易用可能都致使大家把学过的数据结构与算法还回给课本了吧。
标准库定义类顺序容器和关联容器两大类,其主要的区别就是,顺序容器内的元素按其位置实现存储和访问等功能;关联容器内的元素按其按键(key)实现存储和访问、排序等功能。并且,关联容器共享了许多顺序容器提供的操作,此外,它们还定义了自己特殊的操作。
另外,为了更好兼顾各个容器的功能操作,标准库还将与容器操作的一些共性算法抽取处理,作为泛型算法,为容器类型提供通用接口服务。例如,算法库提供了各种各样经典算法的有效实现,像查找、排序及其他常见的算法任务。这些算法可作用于各种不同的容器类型,这样容器提供的操作和算法是一致定义的,接口的一致性使程序变得更灵活,也便于标准库学习。
二、 顺序容器
顺序容器将单一类型元素聚集起来成为容器,然后根据位置来存储和访问这些元素。其的元素排列次序与元素值无关,而是由元素添加到容器里的次序决定。PS:顺序容器的顺序是指元素存储的次序,而非排序。
标准库定义了三种顺序容器类型:数组(vector)、链表(list) 和双端队列( deque),它们的差别在于访问元素的方式,以及添加或删除元素相关操作的运行代价。并根据三种顺序容器所提供的操作,通过定义新的操作接口,创建了容器适配器(adaptors),包括 栈(stack)、队列(queue) 和 priority_queue (优先队列)。前面提到,容器只定义了少量操作,大多数额外操作则由标准库的泛型算法提供。PS:string也可以 视为仅包含字符的特殊顺序容器,string 类型提供大量的容器操作。
容器本质上是模板, 这些容器类型的服务接口差别在于它们提供哪些操作,但是如果两个容器提供了相同的操作,则它们的接口(函数名字和参数个数)应该相同。
template <typename T, typename Alloc = alloc>
class Vector
{...
};
2.1 容器实例化与容器构造函数
所有容器都是类模板,和我们自定义模板一样,使用时需要显式指明模板参数,容器类才能根据实参实例化容器类。并所有容器类型都定义了默认构造函数,用于创建指定类型的空容器对象。默认构造函数不带参数,并创造空对象类,是容器类型最常用的构造函数大多数的程序中,使用默认构造函数能达到最佳运行时性能。
#include <vector>
#include <list>
#include <deque>#include <string>
//vector<std::string> str_vec; //定义保存字符串类型的数组list<int> i_list; //定义保存整数类型的链表deque<double> d_dqs; //定义保存长浮点类型的双端队列
除了默认构造函数,容器类型还提供其他的构造函数,用其他构造函数初始化顺序容器时,无论是直接还是间接,都会指定该容器有多少个元素,并提供这些元素的初值。
//
str_vec.push_back("hi");
vector<string> str_vec_copy(str_vec); //拷贝构造
vector<string> str_vec_size(2); //指定元素个数构造,只适用于顺序容器,元素默认值就是元素类型默认构造
vector<string> str_vec_sVal(2,"hello"); 指定元素个数及元素默然值构造,只适用于顺序容器
vector<string> str_vec_copy_it(str_vec.begin(),str_vec.end()); //拷贝构造,指定复制区间[begin,end)
用其他的构造函数创建的容器,必须确保为同一个容器类型及模板参数类型。
vector<int> ivec;
vector<int> ivec2(ivec); // ok
list<int> ilist(ivec); // error: ivec is not list<int>
vector<double> dvec(ivec); // error: ivec holds int not double
2.2 容器迭代器与拷贝构造
如果确实需要一种类型容器内的元素复制给另一种类型容器内,就需要使用迭代器,标准库允许通过传递一对迭代器间接实现该实现该功能,不要求容器类型相同。容器内的元素类型也可以不相同,只要它们相互兼容,能够将要复制的元素转换为所构建的新容器的元素类型。
vector<string> str_vec_sVal(2,"hello");
list<string> slist(str_vec_sVal.begin(), str_vec_sVal.end());
deque<string> front(str_vec_sVal.begin(), str_vec_sVal.begin()+1);
为何可以这样呢,那是因为迭代器本质上就是特殊的指针,该指针指向顺序容器,而顺序容器本质存储类似数组,顺序容器通过迭代器拷贝,本质上就是数组数据复制。
2.3 自定义容器模板参数类型
作为容器模板参数的元素类型必须是内置或复合类型,或者是提供了默认构造函数的类类型。如果元素类型没有默认构造函数,则必须显式指定其元素初始化式。
class DTest
{public:DTest():val(0){};DTest(int val_):val(val_){};int val;
};//vector<DTest> dt_vec(2,DTest()); //定义保存DTest类型的数组,大小为2,指定默认值为DTest()vector<DTest> dt_vec_cp(dt_vec); //拷贝构造,定义保存DTest类型的数组DTest dt_(10);dt_vec_cp.push_back(dt_);dt_ = dt_vec[0];
对于顺序容器来说,作容器的元素类型。容器元素类型必须满足以下两个约束:
• 元素类型必须支持赋值运算。
• 元素类型的对象必须可以复制。
class DTest
{public:DTest():val(0){};DTest(int val_):val(val_){};int val;private:DTest(DTest const&rhs); //禁止拷贝构造DTest& operator=(const DTest &rhs); //禁止拷贝赋值构造
};//vector<DTest> dt_vec(2,DTest()); //定义保存DTest类型的数组,大小为2,指定默认值为DTest()vector<DTest> dt_vec_cp(dt_vec); //error: 'DTest::DTest(const DTest&)' is privateDTest dt_(10);dt_vec_cp.push_back(dt_);dt_ = dt_vec[0];//error, 'DTest& DTest::operator=(const DTest&)' is private
采用自定义类型作为容器的模板参数必须符合容器类对模板参数类型的要求,尤其是自定义类型是编译器无法创建默认构造、拷贝构造、赋值函数等的情况,例如,自定义类型中包含有指针类型、复杂结构体等情况。
支持复制和赋值功能是容器元素类型的最低要求。此外,不同类型容器操作对元素类型还有特殊要求。如果元素类型不支持这些特殊要求,则相关的容器操作就不能执行,即可以定义该类型的容器,但不能使用某些特定的操作。
2.4 容器的容器
容器类也是数据类型,和普通结构类型一样,容器类也可以作为容器的模板参数,如下图所示,注意用空格隔开两个相邻的 > 符号,以示这是两个分开的符号,否则,系统会认为 >> 是单个符号,为右移操作符,并导致编译时错误
//
vector<string> str_vec; //定义保存字符串类型的数组
vector<vector<string> > str_vecs; //定义保存vector<string>类型的数组
str_vecs.push_back(str_vec);
这时模板参数类型就变成了vector<string>,作为模板参数同样需要满足赋值运算、拷贝构造等约束。所幸容器类作为一个标准化的类,已经提供了这些支持,我们不必担心。
2.5 容器迭代器与指针
每种容器类型都提供若干共同工作的迭代器类型。与容器类型一样,所有迭代器具有相同的接口:如果某种迭代器支持某种操作,那么支持这种操作的其他迭代器也会以相同的方式支持这
种操作。例如,所有容器迭代器都支持以解引用运算从容器中读入一个元素。类似地,容器都提供自增和自减操作符来支持从一个元素到下一个元素的访问。迭代器本质上是指针,例如:
template <typename T, typename Alloc = alloc>
class Vector
{public:typedef T value_type;typedef value_type* pointer;typedef value_type* iterator;typedef value_type* reference;...protected:iterator start;//目前可使用空间开始iterator finsh;//目前可使用空间结尾iterator end_of_storage;//目前可用空间结尾...public:iterator begin(){return start;};...
};
因此在迭代器定义时,主要还是通过确定的容器类及模板实参类确定的
vector<string>::iterator it;
it = str_vec.begin();
2.6 容器迭代器运算操作
标准库为给顺序容器迭代器提供通用运算操作,operator*、operator->、operator++、operator--,operator==、operator!=;针对vector和deque容器,额外提供operator+、operator=、operator+=、operator-=、>/>=/</ <=等运算方法。
//
vector<string>::iterator iter_vec = str_vec.begin() + str_vec.size()/2;//OK
//list<int>::iterator iter_list= i_list.begin() + i_list.size()/2;//error
vector 和 deque 容器为其元素提供快速、随机的访问。它们确保可根据元素位置直接有效地访问指定的容器元素,因此它们的迭代器可以有效地实现算术和关系运算。而list 容器的迭代器既不支持算术运算(加法或减法),也不支持关系运算(<=, <, >=, >),它只提供前置和后置的自增、自减运算以及相等(不等)运算,即前面讲到的通用运算操作。
标准库使用一对迭代器标记迭代器范围(iterator range),两个迭代器分别指向同一个容器中的两个元素或超出末端的下一位置,形成左闭合区间(left-inclusive interval,[...) )。
除了迭代器iterator职务,容器还定义了其他特殊迭代器
template <typename T, typename Alloc = alloc>
class Vector
{public:typedef T value_type;typedef value_type* iterator; //此容器类型的迭代器类型typedef value_type const* const_iterator; //元素的只读迭代器类型typedef value_type* reverse_iterator; //按逆序寻址元素的迭代器typedef value_type const* const_reverse_iterator; //元素的只读(不能写)逆序迭代器...
};
2.7 容器成员操作函数
每种顺序容器都提供了一组有用的类型定义以及以下操作:
• 在容器中添加元素。
• 在容器中删除元素。
• 设置容器大小,是否为空。
• 遍历容器。
以vector容器为例,一般包含以下操作:
1.增加函数void push_back(const T& x):向量尾部增加一个元素Xiterator insert(iterator it,const T& x):向量中迭代器指向元素前增加一个元素xiterator insert(iterator it,int n,const T& x):向量中迭代器指向元素前增加n个相同的元素xiterator insert(iterator it,const_iterator first,const_iterator last):向量中迭代器指向元素前插入另一个相同类型向量的[first,last)间的数据2.删除函数iterator erase(iterator it):删除向量中迭代器指向元素iterator erase(iterator first,iterator last):删除向量中[first,last)中元素void pop_back():删除向量中最后一个元素void clear():清空向量中所有元素3.遍历函数reference at(int pos):返回pos位置元素的引用reference front():返回首元素的引用reference back():返回尾元素的引用iterator begin():返回向量头指针,指向第一个元素iterator end():返回向量尾指针,指向向量最后一个元素的下一个位置reverse_iterator rbegin():反向迭代器,指向最后一个元素reverse_iterator rend():反向迭代器,指向第一个元素之前的位置4.判断函数bool empty() const:判断向量是否为空,若为空,则向量中无元素5.大小函数int size() const:返回向量中元素的个数int capacity() const:返回当前向量所能容纳的最大元素值int max_size() const:返回最大可允许的 vector 元素数量值
在容器中添加元素时,系统是将元素值复制到容器里。类似地,使用一段元素初始化新容器时,新容器存放的是原始元素的副本。被复制的原始值与新容器中的元素各不相关,此后,容器内元素值发生变化时,被复制的原值不会受到影响,反之亦然。
string srt_ = "hello";str_vec.push_back(srt_);srt_ = "change";str_vec.insert(str_vec.begin(),srt_);it = str_vec.begin();while(it!=str_vec.end()){std::cout << *it << std::endl;it++;}
容器类提供push_back、insert函数增加容器元素,push_back直接操作容器类对象,而insert通过容器迭代器间接操作到容器类对象。在容器对象中 insert 或push_back压入一个元素时,该对象的大小增加 1,标准库处理存储这些新元素的内存分配问题。
2.8 容器迭代器失效问题
在 顺序容器中添加元素可能会导致整个容器的重新加载,这样的话,该容器涉及的所有迭代器都会失效。即任何 insert 或 push 操作都可能导致迭代器失效。当编写循环将元素插入到 vector 或 deque 容器中时,程序必须确保迭代器在每次循环后都得到更新。
it = str_vec.begin(); //迭代器先指向string srt_ = "hello";str_vec.push_back(srt_); //容器执行了push_back//it = str_vec.begin(); //while(it!=str_vec.end()) //error,迭代器失效{std::cout << *it << std::endl;it++;}
尤其是在操作迭代器进行遍历容器时 ,如果有删减动作(增加内容也一样),必须要特别注意:
vector<string>::iterator it = str_vec.begin();
while(it!=str_vec.end())
{std::cout << *it << std::endl;if(*it=="hi")str_vec.erase(it); //error,it失效it++;
}
/*------------------------------------------------------------*/
vector<string>::iterator it = str_vec.begin();
vector<string>::iterator last = str_vec.end();
while(it!=last) //error,循环更改了容器内容,last失效
{std::cout << *it << std::endl;if(*it=="hi"){
#ifdef WIN32it = str_vec.erase(it); //重新赋值
#else vector<string>::iterator it_temp = it++;str_vec.erase(it_temp);
#endif//last = str_vec.end();continue;}it++;
}
在使用容器时,建议在每次做完增删运算后重新计算迭代器指向。
it = str_vec.begin();while(it!=str_vec.end()) //循环更改了容器内容,end()可以实时指向新的{std::cout << *it << std::endl;if(*it=="hi"){#ifdef WIN32it = str_vec.erase(it); //重新赋值#else vector<string>::iterator it_temp = it++;str_vec.erase(it_temp);#endifcontinue;}it++;}
2.9 容器类型的关系比较
所有的容器类型都支持用关系操作符来实现两个容器的比较,比较的容器必须具有相同的容器类型,而且其元素类型也必须相同。容器的比较本质上是基于容器内元素的比较。容器的比较使用了元素类型定义的同一个关系操作符:例如,两个容器做 != 比较实际上是使用了其元素类型定义的 != 操作符来逐一比较容器的元素。如果容器模板参数类型不支持某种操作符,则该容器类型就不能做这种比较运算。
vector<string> str_vec; //定义保存字符串类型的数组
str_vec.push_back("hi");
vector<string> str_vec_copy(str_vec); //拷贝构造
std::cout << string((str_vec==str_vec_copy)?"true":"false") << std::endl;//true
str_vec_copy[0]="hil";//hil
std::cout << string((str_vec==str_vec_copy)?"true":"false") << std::endl;//false
std::cout << string((str_vec<=str_vec_copy)?"true":"false") << std::endl;//true
str_vec.push_back("adc");//hi,abc
std::cout << string((str_vec>str_vec_copy)?"true":"false") << std::endl;//false
因此,如果采用自定义类型作为容器的模板参数时,和运算操作符一样,如果想就该容器类型实现对应的关系操作符,就必须提供模板参数类型的对应关系操作符。
//用到了容器的操作符<,就需要先定义模板参数类型的操作符<inline bool operator<(const DTest& obj1, const DTest& obj2) {return obj1.val<obj2.val;};//vector<DTest> dt_vec(2,DTest()); //定义保存DTest类型的数组,大小为2,指定默认值为DTest()vector<DTest> dt_vec_cp(dt_vec); //拷贝构造,定义保存DTest类型的数组std::cout << string((dt_vec<dt_vec_cp)?"true":"false") << std::endl; //falseDTest dt_(10);dt_vec_cp.push_back(dt_);std::cout << string((dt_vec<dt_vec_cp)?"true":"false") << std::endl;//truedt_vec.push_back(DTest(9));std::cout << string((dt_vec<dt_vec_cp)?"true":"false") << std::endl;//true
2.10 容器大小、元素访问与增删
所有容器类型(顺序及关联容器)都提供四种与容器大小相关的操作,size 操作返回容器内元素的个数:empty 操作则返回一个布尔值,当容器的大小为 0 时,返回值为 true,否则为 false。还可以通过resize重新调整容器 c的长度大小,使其能容纳 n 个元素,如果 n <c.size(),则删除多出来的元素;否则,添加采用值初始化的新元素。
//std::cout <<"Container size\n";std::cout << "str_vec.empty() = " << str_vec.empty() << std::endl; //falsestd::cout << "str_vec.size() = " << str_vec.size() << std::endl; //3std::cout << "str_vec.max_size() = " << str_vec.max_size() << std::endl;//178956970str_vec.resize(5);std::cout << "str_vec.size() = " << str_vec.size() << std::endl; //5std::cout << "str_vec.max_size() = " << str_vec.max_size() << std::endl;//178956970str_vec.resize(2);std::cout << "str_vec.size() = " << str_vec.size() << std::endl; //2std::cout << "str_vec.max_size() = " << str_vec.max_size() << std::endl;//178956970
顺序容器访问元素,可以通过迭代器访问,还可以通过front 和 back 成员函数访问特定元素,部分(vector 和 deque 容)可以通过下标访问。
std::cout << "str_vec[0] = " << str_vec[0]<< std::endl;
std::cout << "str_vec.at(0) = " << str_vec.at(0)<< std::endl;
std::cout << "str_vec.front() = " << str_vec.front()<< std::endl;
std::cout << "*(str_vec.begin()) = " << *(str_vec.begin())<< std::endl;
在使用下标访问元素时,需保证在指定下标位置上的元素确实存在。下标操作符本身不会做相关的检查。使用 front 或 back 运算时,如果容器为空,那么这些操作将产生未定义的结果。
前面内容中,展示过erase删除容器元素的代码案例,除此之外,容器类型提供了特定的
pop_front 和 pop_back 操作来删除容器内的元素,以及clear清空容器所有元素。
c.erase(p) 删除迭代器 p 所指向的元素
c.erase(b,e) 删除迭代器 b 和 e 所标记的范围内所有的元素
c.clear() 删除容器 c 内的所有元素。返回 void
c.pop_back() 删除容器 c 的最后一个元素。返回 void。如果 c 为空容器,则该函数未定义
c.pop_front() 删除容器 c 的第一个元素。返回 void。如果 c 为空容器,则该函数未定义,只适用于 list 或 deque 容器
erase的这两种形式都返回一个迭代器,它指向被删除元素或元素段后面的元素。erase 操作也不会检查它的参数。使用者必须确保用作参数的迭代器或迭代器范围是有效的。正如本文前面在循环体里涉及erase 调用一样。
2.11 容器元素交换功能
比起前面简述的这些常用函数操作,顺序容器还提供了一些其他方面函数操作:
void swap(vector&):交换两个同类型向量的数据
void assign(int n,const T& x):设置向量中前n个元素的值为x
void assign(const_iterator first,const_iterator last):向量中[first,last)中元素设置成当前向量元素
swap操作实现交换两个容器内所有元素的功能,该操作不会使迭代器失效,使用时确保操作数必须是相同类型的容器,而且所存储的元素类型也必须相同。完成 swap 操作后,尽管被交换的元素已经存放在另一容器中,但迭代器仍然指向相同的元素。而assign 操作和赋值函数一样,会使左操作数容器的所有迭代器失效。
str_vec.swap(str_vec_copy);std::cout << "str_vec.front() = " << str_vec.front()<< std::endl;std::cout << "str_vec.back() = " << str_vec.back()<< std::endl;str_vec.assign(5, "Hi prfree!");std::cout << "str_vec.size() = " << str_vec.size() << std::endl;std::cout << "str_vec.front() = " << str_vec.front()<< std::endl;
由于swap操作时,容器内没有移动任何元素,因此迭代器不会失效,也因此节省删除元素的成本。
2.12 容器应用选择
在前面已经提到,标准库已经为容器做了内存管理,插入及删除元素时,标准库处理存储这些新元素或删除旧元素的内存分配问题。同时应该也清楚,顺序容器中vector 、deque为了支持快速的随机访问,元素以连续的方式存放,一个元素都紧挨着前一个元素存储。假设当我们在vector 容器内添加一个元素时,如果容器中已经没有空间容纳新的元素,此时,由于元素必须连续存储以便索引访问,所以不能在内存中随便找个地方存储这个新元素。于是,vector 容器必须重新分配存储空间,用来存放原来的元素以及新添加的元素:存放在旧存储空间中的元素被复制到新存储空间里,接着插入新元素,最后撤销旧的存储空间。如果 vector 容器在每次添加新元素时,都要这么分配和撤销内存空间,其性能将会非常慢,简直无法接受。当然只是假设每次新增元素时,连续空间都被占用了的情况。实际中标准库为了使 vector 容器实现快速的内存分配,其实际分配的容量要比当前所需的空间多一些。vector 容器预留了这些额外的存储区,用于存放新添加的元素。
对于不连续存储元素的容器,不存在这样的内存分配问题。例如,在 list 容器中添加一个元素,标准库只需创建一个新元素,然后将该新元素连接在已存在的链表中,不需要重新分配存储空间,也不必复制任何已存在的元素。
list 容器表示不连续的内存区域,允许向前和向后逐个遍历元素。在任何位置都可高效地 insert 或 erase 一个元素。插入或删除 list 容器中的一个元素不需要移动任何其他元素。另一方面,list 容器不支持随机访问,访问某个元素要求遍历涉及的其他元素。
为此通过下面例子测试一下:
//list<int> i_list; //定义保存整数类型的链表const int test_count = 1000000;std::cout << "clock() = " << clock()<< std::endl;for(int i=0; i<test_count ; i++){i_list.push_back(i%3);}for(int i=0; i<test_count ; i++){i_list.pop_back();}std::cout << "clock() = " << clock()<< std::endl;vector<int> i_vec;std::cout << "clock() = " << clock()<< std::endl;for(int i=0; i<test_count ; i++){i_vec.push_back(i%3);}for(int i=0; i<test_count ; i++){i_vec.pop_back();}std::cout << "clock() = " << clock()<< std::endl;
而输出结果却不大敢相信:
clock() = 51
clock() = 263
clock() = 264
clock() = 300
为此,调整代码,不在容器末尾添加元素,而是在容器头部插元素。
//list<int> i_list; //定义保存整数类型的链表const int test_count = 1000000;std::cout << "clock() = " << clock()<< std::endl;for(int i=0; i<test_count ; i++){//i_list.push_back(i%3);i_list.insert(i_list.begin(),i%3);}for(int i=0; i<test_count ; i++){i_list.pop_back();}std::cout << "clock() = " << clock()<< std::endl;vector<int> i_vec;std::cout << "clock() = " << clock()<< std::endl;for(int i=0; i<test_count ; i++){//i_vec.push_back(i%3);i_vec.insert(i_vec.begin(),i%3);}for(int i=0; i<test_count ; i++){i_vec.pop_back();}std::cout << "clock() = " << clock()<< std::endl;
展示的差距一下子就出来了。对于 vector 容器,除了容器尾部外,其他任何位置上的插入(或删除)操作都要求移动被插入(或删除)元素右边所有的元素。
//const int test_count = 1000000;
clock() = 43
clock() = 268
clock() = 269
clock() = 267175//const int test_count = 100;
clock() = 38
clock() = 38
clock() = 39
clock() = 39
deque 容器拥有更加复杂的数据结构,和vector一样,支持对所有元素的随机访问,并从 deque 队列的两端插入和删除元素都非常快,但在容器中间插入或删除付出的代价将更高。
因此:一般而言,元素频繁增删时(除了仅在末尾添加元素无其他增删操作),使用 list 容器优于 vector 容器,尤其时在容器元素个数大的情况下。但是,通常出现的反而是以下情况:对于大部分应用,使用 vector 容器是最好的。原因在于,标准库的实现者使用这样内存分配策略:以最小的代价连续存储元素。由此而带来的访问元素的便利弥补了其存储代价。
另外vector 也不必为每个新元素重新分配容器。所分配的额外内存容量的确切数目因库的实现不同而不同。比起每添加一个新元素就必须重新分配一次容器,这个分配策略带来显著的效率。事实上,其性能非常好,因此在实际应用中,比起 list 和deque 容器,vector 的增长效率通常会更高。
2.13 容器存储空间分配
为此,vector 类提供了两个成员函数:capacity 和reserve 使程序员可与 vector 容器内存分配的实现部分交互工作。capacity操作获取在容器需要分配更多的存储空间之前能够存储的元素总数,而 reserve操作则告诉 vector 容器应该预留多少个元素的存储空间。
//std::cout << "str_vec.size() = " << str_vec.size() << std::endl; //5std::cout << "str_vec.capacity() = " << str_vec.capacity() << std::endl;//5str_vec.push_back("test1");str_vec.push_back("test2");std::cout << "str_vec.size() = " << str_vec.size() << std::endl;//7std::cout << "str_vec.capacity() = " << str_vec.capacity() << std::endl;//10for(int i=0; i<10; i++){str_vec.push_back("test_");}std::cout << "str_vec.size() = " << str_vec.size() << std::endl;//17std::cout << "str_vec.capacity() = " << str_vec.capacity() << std::endl;//20str_vec.reserve(30); std::cout << "str_vec.size() = " << str_vec.size() << std::endl;//17std::cout << "str_vec.capacity() = " << str_vec.capacity() << std::endl;//30str_vec.clear();std::cout << "str_vec.size() = " << str_vec.size() << std::endl;//0std::cout << "str_vec.capacity() = " << str_vec.capacity() << std::endl;//30str_vec.reserve(5); std::cout << "str_vec.size() = " << str_vec.size() << std::endl;//0std::cout << "str_vec.capacity() = " << str_vec.capacity() << std::endl;//30
vector 容器在只要有剩余的容量,就不必为其元素重新分配存储空间。reserve可以指定预留量,但只有在capacity 小于reserve指定量时才其作用,否则capacity 时不会做出调整的。
总的来说,vector 和 deque容器提供了对元素的快速随机访问,类似于内存地址偏移访问一样,但付出的代价是,在容器的任意位置插入或删除元素,比在容器尾部插入和删除的开销更大。list 类型在任何位置都能快速插入和删除,但付出的代价是元素的随机访问开销较大。在实际项目使用中根据具体使用场景配用。
如果无法确定某种应用应该采用哪种容器,则编写代码时尝试只使用 vector 和 lists 容器都提供的操作:使用迭代器,而不是下标,并且避免随机访问元素。这样编写,在必要时,可很方便地将程序从使用 vector 容器修改为使用 list 的容器。
2.14 伪容器string类
前面还提到过string 类型可以看作是模板参数类型为字符的顺序容器,它支持大多数顺序容器操作。除了一些特殊操作,string 类型提供与 vector 容器相同的操作,例如下标访问、迭代器访问、容器大小、增加元素等。string 类型与 vector 容器不同的是,它不支持以栈方式操纵容器:在 string 类型中不能使用 front、back 和 pop_back 操作。
//string s_vec = "hello world";s_vec.push_back('!');string::iterator iter_s = s_vec.begin();while (iter_s != s_vec.end()){cout << *iter_s++; }cout << "\n";for(int i=0; i<s_vec.size(); i++){cout << s_vec[i];}cout << "\n";
下面给出string的大体成员函数,对标一下前面所述的vector,竟然是如此相近,妥妥的类似于vector<char>。
1)字符访问string::at–访问特定字符,带边界检查string::operator[]–访问特定字符string::front–访问第一个字符string::back–访问最后一个字符string::data–访问基础数组,C++11 后与 c_str() 完全相同string::c_str–返回对应于字符串内容的 C 风格零结尾的只读字符串string::substr–以子串构造一个新串;参数为空时取全部源串2)迭代器string::begin–获得指向开始位置的迭代器string::end–获得指向末尾的迭代器string::rbegin–获得指向末尾的逆向迭代器string::rend–获得指向开始位置的逆向迭代器string::cbegin–获得指向开始位置的只读迭代器string::cend–获得指向末尾的只读迭代器string::crbegin–获得指向末尾的逆向只读迭代器string::crend–获得指向开始位置的逆向只读迭代器3)容量大小string::empty–检查是否为空string::size–返回数据的字符长度string::length–返回数据的字符长度,与 size() 完全相同string::max_size–返回可存储的最大的字节容量,在 32 位 Windows 上大概为 43 亿字节。string::reserve–改变 string 的字符存储容量,实际获得的存储容量不小于 reserve 的参数值。string::capacity–返回当前的字符存储容量string::shrink_to_fit(C++11新增)–降低内存容量到刚好4)增改string::clear–清空内容string::insert–插入字符或字符串。目标 string 中的插入位置可用整数值或迭代器表示。如果参数仅为一个迭代器,则在其所指位置插入0值。string::erase–删除 1 个或 1 段字符string::push_back–追加 1 个字符string::pop_back–删除最后 1 个字符,C++11 标准引入string::append–追加字符或字符串string::operator+=–追加,只有一个参数——字符指针、字符或字符串;不像 append() 一样可以追加参数的子串或若干相同字符string::copy–拷贝出一段字符到 C 风格字符数组;有溢出危险string::resize–改变(增加或减少)字符串长度;如果增加了字符串长度,新字符缺省为 0 值string::swap–与另一个 string 交换内容string::replace–替换子串;如果替换源数据与被替换数据的长度不等,则结果字符串的长度发生改变5)搜索string::find–前向搜索特定子串的第一次出现string::rfind–从尾部开始,后向搜索特定子串的第一次出现string::find_first_of–搜索指定字符集合中任意字符在 *this中的第一次出现string::find_last_of–搜索指定字符集合中任意字符在 *this 中的最后一次出现string::find_first_not_of–*this 中的不属于指定字符集合的首个字符string::find_last_not_of–*this 中的不属于指定字符集合的末个字符string::compare–与参数字符串比较
二、容器适配器
标准库基于vector、list、deque容器,提供了三种顺序容器适配器:queue、priority_queue 和 stack。适配器(adaptor)是标准库中通用的概念,包括容器适配器、迭代器适配器和函数适配器。本质上,适配器是使一事物的行为类似于另一事物的行为的一种机制,类似于我们说的适配器设计模式。容器适配器让一种已存在的容器类型采用另一种不同的抽象类型的工作方式实现。
默认的 stack 和 queue 都基于 deque 容器实现,而 priority_queue 则在 vector 容器上实现。
2.1 stack容器适配器
例如我们可以通过类模板定义仿标准库定义stack类观察它实现原理:
template <typename T>
class MyStack
{private:std::deque<T> elems;public:void push(T const &val);void pop();T top() const;bool empty() const;bool clear();//template <typename T1>MyStack<T>& operator=(MyStack<T1> const&);
};
template <typename T>
void MyStack<T>::push(T const &val)
{elems.push_front(val);//栈顶增加
};template <typename T>
void MyStack<T>::pop()
{elems.pop_front();//栈顶先出
};template <typename T>
T MyStack<T>::top() const
{return elems.front();//优先取栈顶元素
};template <typename T>
bool MyStack<T>::empty() const
{return elems.empty();//
};template <typename T>
bool MyStack<T>::clear()
{return elems.clear();
};template <typename T>
template <typename T1>
MyStack<T>& MyStack<T>::operator=(MyStack<T1> const&rhs)
{if((void*)this==(void*)&rhs) //禁止赋值自身{return *this;}MyStack<T1> tmp(rhs);elems.clear();while(!tmp.empty()){elems.push_back(tmp.top());//先进后出,保持和右值容器次序一致tmp.pop();}return *this;
};//MyStack<int> i_mystack;MyStack<float> f_mystack;i_mystack.push(100);i_mystack.push(200);f_mystack = i_mystack; //Ok,int型被转换为floatstd::cout << "f_mystack.top() = " << f_mystack.top() << std::endl;
所有容器适配器都根据其基础容器类型所支持的操作来定义自己的操作。上述的类模板以deque容器为基础,通过按栈的行为重新设计操作行为,提供栈特性的处理能力,将双端队列操作行为的容器适配为栈行为的容器。stack 栈可以建立在vector、list 或者 deque 容器之上。而 deque容器提供 push_front 运算,可以在集合的另一端插入元素,很好地满足栈的特性,因此优先选择了建立在deque 容器上。这里只是抛砖引玉,标准库做了类似的实现,只是提供了更加丰富、更加完善的功能。
2.2 队列及优先队列容器适配器
标准库队列使用了先进先出(FIFO)的存储和检索策略。进入队列的对象被放置在尾部,下一个被取出的元素则取自队列的首部。标准库提供了两种风格的队列:FIFO 队列(FIFO queue,简称 queue),以及优先级队列(priority queue)。两者提供了类似的操作,不查看源码,我们并不会想到它们是基于不同的原型容器实现的,它们统一由 《queue》标准库文件提供,主要区别就是,优先队列在容器进入队列时会按排序加入队列。
q.empty() 如果队列为空,则返回 true,否则返回 false
q.size() 返回队列中元素的个数
q.pop() 删除队首元素,但不返回其值
q.front() 返回队首元素的值,但不删除该元素
该操作只适用于队列
q.back() 返回队尾元素的值,但不删除该元素
该操作只适用于队列
q.top() 返回具有最高优先级的元素值,但不删除该元素
该操作只适用于优先级队列
q.push(item) 对于 queue,在队尾压入一个新元素,对于 priority_quue,在
基于优先级的适当位置插入新元素
priority_queue 允许用户为队列中存储的元素设置优先级。这种队列不是直接将新元素放置在队列尾部,而是放在比它优先级低的元素前面。标准库默认使用元素类型的 < 操作符来确定它们之间的优先级关系,当在队列push元素时,优先队列会自动依据元素类型<操作符给其排序。
#include <queue>
class DTest
{public:DTest():val(0){};DTest(int val_):val(val_){};int val;private://DTest(DTest const&rhs); //禁止拷贝构造//DTest& operator=(const DTest &rhs); //禁止拷贝赋值构造
};inline bool operator<(const DTest& obj1, const DTest& obj2)
{return obj1.val<obj2.val;
};//priority_queue<DTest> pque;//push元素,注意DTest类型是数越大优先级越高pque.push(23);pque.push(15);pque.push(18);pque.push(21);while (!pque.empty()){cout << pque.top().val << " "; //23 21 18 15pque.pop();}cout << "\n";
其实,容器定义的操作非常少,只定义了构造函数、添加或删除元素的操作、设置容器长度的操作以及返回指向特殊元素的迭代器的操作。其他一些有用的操作,如排序、查找,则不是由容器类型定义,标准库是通过标准泛型算法为容器额外提供的,待续。
三、测试源码补充
创建test.h/cpp源文件,运行g++ test.cpp -o test.exe指令编译,运行输出程序:
test.h
#ifndef _TEST_H_
#define _TEST_H_#include <vector>
#include <list>
#include <deque>
#include <queue>class DTest
{public:DTest():val(0){};DTest(int val_):val(val_){};int val;private://DTest(DTest const&rhs); //禁止拷贝构造//DTest& operator=(const DTest &rhs); //禁止拷贝赋值构造
};inline bool operator<(const DTest& obj1, const DTest& obj2)
{return obj1.val<obj2.val;
};template <typename T>
class MyStack
{private:std::deque<T> elems;public:void push(T const &val);void pop();T top() const;bool empty() const;bool clear();//template <typename T1>MyStack<T>& operator=(MyStack<T1> const&);
};
template <typename T>
void MyStack<T>::push(T const &val)
{elems.push_front(val);//栈顶增加
};template <typename T>
void MyStack<T>::pop()
{elems.pop_front();//栈顶先出
};template <typename T>
T MyStack<T>::top() const
{return elems.front();//优先取栈顶元素
};template <typename T>
bool MyStack<T>::empty() const
{return elems.empty();//
};template <typename T>
bool MyStack<T>::clear()
{return elems.clear();
};template <typename T>
template <typename T1>
MyStack<T>& MyStack<T>::operator=(MyStack<T1> const&rhs)
{if((void*)this==(void*)&rhs) //禁止赋值自身{return *this;}MyStack<T1> tmp(rhs);elems.clear();while(!tmp.empty()){elems.push_back(tmp.top());//先进后出,保持和右值容器次序一致tmp.pop();}return *this;
};#endif //_TEST_H_
test.cpp
#include "test.h"
#include <string>
#include <iostream>
#include <time.h>using namespace std;int main(int argc, char* argv[])
{std::cout <<"Container define\n";vector<string> str_vec; //定义保存字符串类型的数组list<int> i_list; //定义保存整数类型的链表deque<double> d_dqs; //定义保存长浮点类型的双端队列//std::cout <<"Container construction\n";str_vec.push_back("hi");vector<string> str_vec_copy(str_vec); //拷贝构造std::cout << string((str_vec==str_vec_copy)?"true":"false") << std::endl;str_vec_copy[0]="hil";std::cout << string((str_vec==str_vec_copy)?"true":"false") << std::endl;std::cout << string((str_vec<=str_vec_copy)?"true":"false") << std::endl;//str_vec.push_back("adc");std::cout << string((str_vec>str_vec_copy)?"true":"false") << std::endl;vector<string> str_vec_size(2); //指定元素个数构造,只适用于顺序容器,元素默认值就是元素类型默认构造vector<string> str_vec_sVal(2,"hello"); 指定元素个数及元素默然值构造,只适用于顺序容器vector<string> str_vec_copy_it(str_vec.begin(),str_vec.end()); //拷贝构造,指定复制区间[begin,end)//std::cout <<"construction from other Container type\n";//vector<string> str_vec_sVal(2,"hello"); list<string> slist(str_vec_sVal.begin(), str_vec_sVal.end());deque<string> front(str_vec_sVal.begin(), str_vec_sVal.begin()+1);//std::cout <<"construction from other user def type\n";vector<DTest> dt_vec(2,DTest()); //定义保存DTest类型的数组,大小为2,指定默认值为DTest()vector<DTest> dt_vec_cp(dt_vec); //拷贝构造,定义保存DTest类型的数组std::cout << string((dt_vec<dt_vec_cp)?"true":"false") << std::endl;DTest dt_(10);dt_vec_cp.push_back(dt_);std::cout << string((dt_vec<dt_vec_cp)?"true":"false") << std::endl;dt_ = dt_vec[0];dt_vec.push_back(DTest(9));std::cout << string((dt_vec<dt_vec_cp)?"true":"false") << std::endl;//std::cout <<"construction is template arg type\n";vector<vector<string> > str_vecs; //定义保存vector<string>类型的数组str_vecs.push_back(str_vec);//std::cout <<"Container iterator type\n";vector<string>::iterator it;it = str_vec.begin();//vector<string>::iterator iter_vec = str_vec.begin() + str_vec.size()/2; //OK//list<int>::iterator iter_list= i_list.begin() + i_list.size()/2; //error//std::cout <<"Container comment is copy data\n";string srt_ = "hello";str_vec.push_back(srt_);srt_ = "change";str_vec.insert(str_vec.begin(),srt_);it = str_vec.begin();while(it!=str_vec.end()){std::cout << *it << std::endl;it++;}//std::cout <<"Container comment is change for iterator\n";it = str_vec.begin();while(it!=str_vec.end()) //循环更改了容器内容,end()可以实时指向新的{std::cout << *it << std::endl;if(*it=="hi"){#ifdef WIN32it = str_vec.erase(it); //error#else vector<string>::iterator it_temp = it++;str_vec.erase(it_temp);#endifcontinue;}it++;}std::cout <<"iterator traversing \n";it = str_vec.begin();while(it!=str_vec.end()){std::cout << *it << std::endl;it++;}//std::cout <<"Container size\n";std::cout << "str_vec.empty() = " << str_vec.empty() << std::endl;std::cout << "str_vec.size() = " << str_vec.size() << std::endl;std::cout << "str_vec.max_size() = " << str_vec.max_size() << std::endl;str_vec.resize(5);std::cout << "str_vec.size() = " << str_vec.size() << std::endl;std::cout << "str_vec.max_size() = " << str_vec.max_size() << std::endl;str_vec.resize(2);std::cout << "str_vec.size() = " << str_vec.size() << std::endl;std::cout << "str_vec.max_size() = " << str_vec.max_size() << std::endl;//std::cout <<"Container comment get it\n";std::cout << "str_vec[0] = " << str_vec[0]<< std::endl;std::cout << "str_vec.at(0) = " << str_vec.at(0)<< std::endl;std::cout << "str_vec.front() = " << str_vec.front()<< std::endl;std::cout << "*(str_vec.begin()) = " << *(str_vec.begin())<< std::endl;//std::cout <<"Container other func test\n";str_vec.swap(str_vec_copy);std::cout << "str_vec.front() = " << str_vec.front()<< std::endl;std::cout << "str_vec.back() = " << str_vec.back()<< std::endl;str_vec.assign(5, "Hi prfree!");std::cout << "str_vec.size() = " << str_vec.size() << std::endl;std::cout << "str_vec.front() = " << str_vec.front()<< std::endl;////const int test_count = 1000000;const int test_count = 100;std::cout << "clock() = " << clock()<< std::endl;for(int i=0; i<test_count; i++){//i_list.push_back(i%3);i_list.insert(i_list.begin(),i%3);}for(int i=0; i<test_count; i++){i_list.pop_back();}std::cout << "clock() = " << clock()<< std::endl;vector<int> i_vec;std::cout << "clock() = " << clock()<< std::endl;for(int i=0; i<test_count; i++){//i_vec.push_back(i%3);i_vec.insert(i_vec.begin(),i%3);}for(int i=0; i<test_count; i++){i_vec.pop_back();}std::cout << "clock() = " << clock()<< std::endl;//std::cout << "str_vec.size() = " << str_vec.size() << std::endl; //5std::cout << "str_vec.capacity() = " << str_vec.capacity() << std::endl;//5str_vec.push_back("test1");str_vec.push_back("test2");std::cout << "str_vec.size() = " << str_vec.size() << std::endl;//7std::cout << "str_vec.capacity() = " << str_vec.capacity() << std::endl;//10for(int i=0; i<10; i++){str_vec.push_back("test_");}std::cout << "str_vec.size() = " << str_vec.size() << std::endl;//17std::cout << "str_vec.capacity() = " << str_vec.capacity() << std::endl;//20str_vec.reserve(30); std::cout << "str_vec.size() = " << str_vec.size() << std::endl;//17std::cout << "str_vec.capacity() = " << str_vec.capacity() << std::endl;//30str_vec.clear();std::cout << "str_vec.size() = " << str_vec.size() << std::endl;//0std::cout << "str_vec.capacity() = " << str_vec.capacity() << std::endl;//30str_vec.reserve(5); std::cout << "str_vec.size() = " << str_vec.size() << std::endl;//0std::cout << "str_vec.capacity() = " << str_vec.capacity() << std::endl;//5//string s_vec = "hello world";s_vec.push_back('!');string::iterator iter_s = s_vec.begin();while (iter_s != s_vec.end()){cout << *iter_s++; }cout << "\n";for(int i=0; i<s_vec.size(); i++){cout << s_vec[i];}cout << "\n";//MyStack<int> i_mystack;MyStack<float> f_mystack;i_mystack.push(100);i_mystack.push(200);f_mystack = i_mystack;std::cout << "f_mystack.top() = " << f_mystack.top() << std::endl;//priority_queue<DTest> pque;//push元素,注意DTest类型是数越大优先级越高pque.push(23);pque.push(15);pque.push(18);pque.push(21);while (!pque.empty()){cout << pque.top().val << " "; pque.pop();}cout << "\n";return 0;
};
相关文章:

c/c++开发,无可避免的模板编程实践(篇四)
一、容器与模板 前文就说到,标准库基于模板编程,定义了许多容器类以及一系列泛型算法,使程序员可以更简洁、抽象和有效地编写程序。C标准库中有大量的标准容器,这些容器通常包含一组数据或对象的集合,几乎可以和任何类…...

c++11 标准模板(STL)(std::unordered_set)(二)
定义于头文件 <unordered_set> template< class Key, class Hash std::hash<Key>, class KeyEqual std::equal_to<Key>, class Allocator std::allocator<Key> > class unordered_set;(1)(C11 起)namespace pmr { templ…...

GEE学习笔记 七十二:【GEE之Python版教程六】命令行简介
这篇开始就要讲解GEE相关的内容,首先聊一下命令行的内容,这个在官方文档中有详细的介绍,这里我简单说一下常用的几个命令,剩余的大家在使用过程中如果又需要可以随时查看相关官方文档的介绍。官方文档地址:https://dev…...

DDD单根 聚合根 实体 值对象
前言2004年Eric Evans 发表Domain-Driven Design –Tackling Complexity in the Heart of Software (领域驱动设计),简称Evans DDD。快二十年的时间,领域驱动设计在不断地发展,后微服务时代强调的东西,在国…...

SpringMvc介绍。
目录 1、SpringMvc概述 1、基本介绍 2、工作流程 3、bean加载控制 二、请求 1、请求映射路径 2、请求方式 3、请求参数 4、请求参数(传递json数据) 5、日期类型参数传递 三、响应 四、REST风格 1、REST简介 2、RESTful入门案例 3、RESTfu…...

华为OD机试 - 最小传递延迟(JS)
最小传递延迟 题目 通讯网络中有N个网络节点 用1 ~ N进行标识 网络通过一个有向无环图进行表示 其中图的边的值,表示节点之间的消息传递延迟 现给定相连节点之间的延时列表times[i]={u,v,w} 其中u表示源节点,v表示目的节点,w表示u和v之间的消息传递延时 请计算给定源节点到…...

学生信息管理系统(通讯录)----------通俗易懂、附源码、C语言实现
绪论: 本篇文章使结构体章节后的习题,如果你对C语言有问题,或者结构体有什么问题不妨看看我之前所写的文章(章回体),对于文件管理和内存分配问题我将在后面补上,对于这个学生信息管理系统我用了多种方法和…...

Python抽奖系统
#免费源码见文末公众号# 抽奖系统① def choujiang1():def write():with open(d:\\抽奖系统\\抽奖1.1.pickle,rb) as file:lst1pickle.load(file)namevar1.get()if name not in lst1 and name!录入成功! and name!录入失败! and name!:lst1.append(name)…...

真实景观渲染技巧【Three.js】
受到一些很棒的 three.js 演示、与 covid 相关的旅行禁令以及可能在 pinterest 上花太多时间看美丽的旅行照片的启发——我开始看看我是否可以使用 three.js 和r3f在浏览器中渲染一个令人信服的风景场景。 推荐:将 NSDT场景编辑器 加入你的3D开发工具链。 在过去一个…...

MySQL知识汇总:MySQL函数CASE WHEN用法详解
Case When的两种简单用法 用法一: CASE seasonWHEN Spring THEN 春天 WHEN Summer THEN 夏天 WHEN autumn THEN 秋天 else 冬天 end 用法二: CASE WHEN season Spring THEN 春天WHEN season Summer THEN 夏天WHEN season autumn THEN 秋天 els…...

Python学习-----模块1.0(模块的简介、定义与使用)
目录 前言: 1.什么是模块 2.模块的分类 (1)内置模块 (2)第三方模块 (3)自定义模块 3.模块的使用 4.自定义模块 5.模块和执行文件的判断 前言: 今天就开始讲Python中的模块篇…...

Linux进程学习【二】
✨个人主页: Yohifo 🎉所属专栏: Linux学习之旅 🎊每篇一句: 图片来源 🎃操作环境: CentOS 7.6 阿里云远程服务器 Perseverance is not a long race; it is many short races one after another…...

我问chatGPT,在JavaScript中构造函数和类的区别
问:构造器函数和面向中的类是同样的东西吗|? 答:构造器函数和面向对象中的类并不是同样的东西,它们之间有些许不同。 在面向对象编程中,类是一种抽象的概念,它描述了一类具有相同属性和行为的对象。类可以…...

软考高级-信息系统管理师之沟通管理(最新版)
项目沟通管理 1、项目沟通管理基础项目沟通管理的重要性项目沟通管理相关理论2、规划沟通管理3、管理沟通4、控制沟通项目沟通管理的技术和工具1、项目沟通管理基础 项目沟通管理的重要性 1、与1T项目成功有关的最重要的四个因素是:主管层的支持、用户参与、有经验的项目经理…...

PyQt5 自定义富文本编辑器
介绍 一款使用PyQt5和网页端框架wangEditor集成的富文本编辑器 代码片段 PyQt5客户端 与网页端建立连接def create_connect(self):self.web_view QWebEngineView()self.bridge JSBridge(self.web_view.page())self.web_view.load(QUrl.fromLocalFile(self.editor_path))w…...

【高可用系统架构设计】SLA服务可用性4个9是什么意思?如何保证服务的高可用性 HA(High Availability)?...
如何保证服务的高可用性 HA(High Availability)?高可用HA(High Availability)是分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计减少系统不能提供服务的时间。方法论上,高可用是通…...

微服务架构设计模式-(14)面向生产环境
生产环境要求 应用安全 数据权限 服务可配置性 不同环境的配置不一样,不能写死代码,所以要配置 可观测性 需要日志系统 应用安全 四个方面 身份验证 验证主体的身份解决方案 单体 cookie 微服务中 API Gateway 访问令牌 不透明令牌透明令牌ÿ…...

HTML5新增用法
新增语义化标签 并无特殊含义,是语义!语义!语义! <header> 头部区域 <nav> 导航区域 <main> 主体区域 <article> 内部标签 <section> 块级标签 <aside> 侧边栏标签 <footer> 尾部…...

富足金字塔:人的努力是为了扩大选择的范围
人的努力是为了扩大选择的范围,这是熵减的另一种表述。富足金字塔代表着人生的三重境界。第一层是温饱。人需要食物、水、住所。第二层是品质。能源、ICT、教育带来更有品质的生活,如智能门锁、智能马桶、扫地机、洗碗机、洗衣烘衣机。第三层是梦想。包括…...

C++类基础(十七)
类的继承——补充知识 ● public 与 private 继承(C Public, Protected and Private Inheritance) 改变了类所继承的成员的访问权限 //公有继承 struct Base { public:int x; private:int y; protected:int z; }; struct Derive : public Base //公有继承…...

LeetCode刷题复盘笔记—一文搞懂贪心算法之56. 合并区间(贪心算法系列第十四篇)
今日主要总结一下可以使用贪心算法解决的一道题目,56. 合并区间 题目:56. 合并区间 Leetcode题目地址 题目描述: 以数组 intervals 表示若干个区间的集合,其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间…...

Andriod入门级开发
这学期有个课设,我们组我负责一个手机APP的开发,虽然刚开始说要实现什么智能导航,类似高德地图那种,但最后阉割的只剩一个Socket通信了,因为之前没有接触过(可能之后也不会再接触),记…...

DCL 数据控制语言
1、简介 DCL英文全称是Data Control Language(数据控制语言),用来管理数据库用户、控制数据库的访问权限。 2、管理用户 2.1 查询用户 select * from mysql.user;查询的结果如下: 其中 Host代表当前用户访问的主机, 如果为localhost, 仅代表只能够在当前本机访问…...

全网超详细的下载与安装VMware虚拟机以及为什么要安装VMware虚拟机
文章目录1. 文章引言2. 下载VMware3. 安装VMware1. 文章引言 我们使用最多的系统是windows系统,因为,国内电脑厂商的操作系统(os)基本是windows系统,比如华为、联想、华硕等电脑。 但线上的服务器大多是Linux系统,而我们经常使用…...

Python获取zabbix问题触发器
背景:阿里云的ECS服务器因为阿里云升级插件,导致安全防护程序重启,产生不同的端口。导致低自动发现注册的端口 大量报警。 解决:杀掉关于因为非业务 变更的端口检测的触发器。 相关文档: Zabbix监控之主机端口监控自…...

原型链污染
目录 前置知识 原型对象 prototype和__proto__的区别 原型链概念 原型链的继承 原型 链污染 原型链污染原理 javascript中可能会存在原型链污染的危险函数 原型链污染的实际应用 JavaScript中可以触发弹窗的函数 前置知识 原型对象 在JavaScript中,每个函…...

ClickHouse详解
一、概念ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS)。OLAP场景的关键特征绝大多数是读请求数据以相当大的批次(> 1000行)更新,而不是单行更新;或者根本没有更新。已添加到数据库的数据不能修改。对于读取,从数据库中提取相当多的…...

02_Docker 安装
02_Docker 安装 文章目录02_Docker 安装2.1 安装 Docker 的先决条件2.2 在 Ubuntu 和 Debain 中安装 Docker2.2.1 检查前提条件1. 内核2.检查 Device Manager2.2 安装 DockerDocker 支持非常多的Linux平台,包括Ubuntu和RHEL,除此之外,Docker还…...

K8S集群将Docker切换到Containerd
文章目录1. 开启节点维护1.1 将节点设置成不可调度1.2 驱逐节点上的 Pod1.3 停止相关服务2. 升级到 containerd2.1 安装 containerd2.2 调整 containerd 配置2.3 修改 kubelet 启动配置参数3. 重启节点服务4. 验证升级后的节点5. 容器管理工具5.1 容器管理命令行工具对比5.2 cr…...

Kubernetes03:kubernetes 功能和架构
2.1 概述 Kubernetes 是一个轻便的和可扩展的开源平台,用于管理容器化应用和服务。通过 Kubernetes 能够进行应用的自动化部署和扩缩容。在 Kubernetes 中,会将组成应用的容 器组合成一个逻辑单元以更易管理和发现。Kubernetes 积累了作为 Google 生产环…...