泛型编程思想最早缘于A.Stepanov提出的部分算法可独立于数据结构的论断。20世纪90年代初A.Stepanov和Meng Lee根据泛型编程的理论用C++共同编写了STL。但直至1998年,STL才成为C++的正式标准。在后来的几年中,各大主流编译器也都相继加入了对STL的支持,至此STL才开始得到广泛的应用。
STL体现的是泛型编程的核心思想:独立数据结构和算法(这是一种独立于OO的编程哲学)。
STL主要由几个核心部件组成,即迭代器、容器、算法、函数对象、适配器。容器即物之所属;算法是解决问题的方式;迭代器是对容器的访问逻辑的抽象,是连接算法和容器的纽带;迭代器通过添加了一种间接层的方式实现了容器和算法之间的独立;函数对象,就是重载了operator()操作符的对象;适配器是通过组合特定的容器实现的一种新的数据结构。在后续的内容中,我们将对几个核心部件的基础应用进行详细的描述。
C++产生的历史背景赋予了C++太多的职责,比如兼容C、综合的编程语言等,这些虽然赋予了C++强大的功能,但同时也扔给了极大的复杂度。在这篇文章中,我们并不打算将你带入C++的复杂地带,但是需要你有一定的C++基础,比如类、结构等。
STL深深地植根于C++的基础设施,这其中包括了内联、函数对象、函数模板、类模板等。
内联是C++中一种特殊的语言机制,使用inline来标识。C++在编译inline标识的函数时,将根据特定的规则将inline函数的代码直接插入到inline函数的调用位置,以此来提高程序的运行效率,但同时也在一定程度上导致了代码的膨胀。请看下面的例子:
inline全局函数
inline void max{…};
inline成员函数
class A{
public:
inline void max{…};
}
函数对象,就是重载了operator()操作符的对象。相对函数指针,函数对象可以具有状态,更安全,更灵活,基本没有额外的开销。在STL中,函数对象经常被用作算法的输入参数或容器的实例化的参数。请看下面的例子:
定义函数对象类
classs LessThan{
public:
LessThan(int val): m_val(val){}
bool operator()(int val){return m_val < val;}
private:
int m_val;
};
定义函数对象
LessThan less(5);
调用定义函数对象
less(10);//返回为true
C++中的模板是STL实现的技术基础。C++中的模板可分为函数模板和类模板。函数模板抽象了针对不同类型的同一性质的操作。如针对int/long/string的max操作。请看下面的例子:
定义求取两个类型的值或对象的最大值的操作
template<typename T>
T max(T a,T b){return a>b ? a : b;}
求取两个int值的最大值
max(0,10);//返回10
求取两个string对象的最大值
max(string(“Hello”),string(“world”));//返回string(“world”)
C++中的模板是STL实现的技术基础。C++中的模板可分为函数模板和类模板。类模板抽象了针对不同类型的同一类事务。如针对int/long/string的Stack。请看下面的例子:
定义一个通用堆栈Stack
template<typename T>
class Stack{
public:
inline void push(const T& value){…}
T pop(){…}
void clear(){…}
bool empty() const {…}
};
声明一个int类型的Stack
typdef Stack<int> IntStack;
声明一个string类型的Stack
typdef Stack<string> IntStack;
STL中的迭代器是C++指针的泛化,它在算法和容器之间充当一个中间层,为处理不同的数据结构提供了一致的方式。迭代器也可以看作是对容器数据结构访问的一种约束。STL中的迭代器可分为类:随机存取迭代器(random-access-iterator),双向存取迭代器(bidirectional-access-iterator),前向迭代器(forward iterator),输入迭代器(input-iterator),输出迭代器(output-iterator)。它们之间的继承关系如下图:
input-iterator
output-iteartor
forward-iterator:output-iteartor , input-iterator
bidirectional-access-iterator : forward-iterator
random-access-iterator:bidirectional-access-iterator
图一 迭代器关系图
输入迭代器也可以称之为前向的只读访问器,首先它提供了对容器的只读访问,其次它只能在容器中进行前向迭代(即只提供++操作)。所有的容器的迭代器都具有输入迭代器的特征。通过输入迭代器你可以进行下面三种操作:
1. V = *X++
2. V = *X,X++
3. V = *X,++X
注:V为值,X为迭代器
输出迭代器也可以称之为前向的只写访问器,首先它提供了对容器的只写访问,其次它只能在容器中进行前向迭代(即只提供++操作)。通过输出迭代器你可以进行下面三种操作:
1. *X++ = V
2. *X = V, X++
3. *X = V, ++X
注:V为值,X为迭代器
前向迭代器继承自输入和输出迭代器,因此具有输入和输出迭代器的所有特征,也即提供对容器数据结构的读写访问但也只具有前向迭代的能力(即只提供++操作)。因此,你可以对前向迭代器进行操作:R == S /++R == ++S。
请看下面的例子:
定义一个利用前向迭代器进行线性查找的算法
template<typename ForwardIterator,typename T>
ForwardIterator linear_search(ForwardIterator first,ForwardIterator last,const T& value)
{
for (; first != last; ++first){
if (*first == value)
return first;
}
return last;
}
测试
int ia[] = {35,3,23};
vector<int> vec(ia,ia+3);
vector<int>::iterator it = linear_search(vec.begin(),vec.end(),100);
if (it != vec.end())
std::cout << "Found" << endl;
else
std::cout << "Not Found" << endl;
测试结果为:Not Found
双向存取迭代器从前向迭代器继承过来,因而具有前向迭代器的所有特征,双向存取迭代器还具有后向访问能力(即只提供--操作)。请看下面的例子:
定义一个利用双向迭代器排序算法
template<typename BidirectionalIterator,typename Compare>
void sort_me(BidirectionalIterator first,BidirectionalIterator last,Compare comp)
{
for(BidirectionalIterator i = first; i != last; ++i)
{
BidirectionalIterator _last = last;
while(i != _last--)
{
if (comp(*i,*_last))
iter_swap(i,_last);
}
}
}
测试
int ia[] = {123,343,12,100,343,5,5};
vector<int> vec(ia,ia+7);
sort_me(vec.begin(),vec.end(),less<int>());
copy(vec.begin(),vec.end(),ostream_iterator<int>(cout," "));
std::cout << endl;
测试结果为:5 5 12 100 123 343 343
随机存取迭代器从双向存取迭代器继承过来,因而具有双向存取迭代器的所有特征。所不同的是,利用随机存取迭代器你可以对容器数据结构进行随机访问,因而随机存取迭代器还可以定义下面的操作:
1. operator+(int)
2. operator+=(int)
3. operator-(int)
4. operator-=(int)
5. operator[](int)
6. operator-(random-access-iterator)
7. operator>(random-access-iterator)
8. operator<(random-access-iterator)
9. operator>=(random-access-iterator)
10. operator<=(random-access-iterator)
在STL中,随机存取双向迭代器只能作用于顺序容器。请看下面的例子:
测试输出vector里面的数据
int ia[] = {123,343,12,100,343,5,5};
vector<int> vec(ia,ia+7);
for(int i = 0; i < vec.size(); ++i)
std::cout << vec[i] << " ";
测试结果为:123 343 12 100 343 5 5
容器即物之所在。容器是STL的核心部件之一,是迭代器的依附,是算法作用的目标。
STL中的容器可分为顺序容器(Sequence Container)和关联容器(Associative Container)。容器适配器(Container Adaptor)是对顺序容器(Sequence Container)或关联容器(Associative Container)进行包装而得到的一种具有更多约束力(或功能更强大)的容器。
下表列出的是STL中的主要(标准和非标准的)容器:
顺序容器
(Sequence Container)
|
容器 |
备注 |
vector |
|
stack |
非STL标准,
vector的适配器(Adaptor)
|
list |
|
slist |
非STL标准,
list的适配器(Adaptor)
|
deque |
|
priority_queue |
非STL标准,
deque的适配器(Adaptor)
|
queue |
非STL标准,
deque的适配器(Adaptor)
|
关联容器
(Associative Container)
|
set |
底层数据结构是RB-tree(红黑树) |
multiset |
底层数据结构是RB-tree(红黑树) |
map |
底层数据结构是RB-tree(红黑树) |
multimap |
底层数据结构是RB-tree(红黑树) |
hashtable |
非STL标准 |
hash_set |
非STL标准,底层数据结构是hashtable |
hash_map |
非STL标准,底层数据结构是hashtable |
hash_multiset |
非STL标准,底层数据结构是hashtable |
hash_multimap |
非STL标准,底层数据结构是hashtable) |
所有的容器都是物之所在,这就决定了它们必然存在很多共性,这些共性包括迭代器、大小等属性。容器与容器之间的主要区别体现在对数据的操作上。
每类容器都包含四个迭代器:iterator(正向迭代器)、const_iterator(常正向迭代器)、reverse_iterator(反向迭代器)、const_reverse_iterator(常反向迭代器)。因此你可以按照下面的方式获取每个容器的相应的迭代器:
获取正向迭代器
C<T>::iterator it = c.begin();
C <T>::iterator it = c.end();
获取反向迭代器
C <T>::reverse_iterator it = c.rbegin();
C <T>:: reverse_iterator it = c.rend()
获取常正向迭代器
C<T>::const_iterator it = c.begin();
C <T>:: const_iterator it = c.end();
获取常反向迭代器
C <T>:: const_ reverse_iterator it = c.rbegin();
C <T>:: const_ reverse_iterator it = c.rend()
注:C为容器类型,c为C的实例
所有容器是数据的存在之处,可以看作的是数据的集合,因此它们都会有大小、是否为空等属性,因此你可以按照下面的方式获取所有的容器的公共属性:
获取容器的大小
c.size();
判断容器是否为空
c.empty();
顺序容器中所有的元素在容器中的物理位置都是按照特定的次序进行存放的,区别于关联容器的是顺序容器中的元素的位置都是既定的。被纳入STL标准的顺序容器包括vector、list、dequeue。
序列容器之间的共性除了容器之间应有的共性之外,还有对数据操作的接口(非实现)上:
c.push_back
c.pop_back
c.push_front
c.pop_front
c.back
c.front
c.erase
c.remove
vector和数组具有同样的内存处理方式。不同于数组的是:数组是静态空间,一旦分配了就不能被改变,因而空间的分配非常地不灵活;vector是动态空间,即空间可以被动态分配,因而空间的分配很灵活。可以说vector是相对数组的一种更高级的数据结构。
vector中的迭代器的种类为随机存取迭代器(random-access-iterator)。
vector不同于其它顺序容器(Sequence Container) 的时,它具有capacity属性,可以通过vec.capacity()来获取。
请看下面的例子:
构造vector
int ia[] = {123,343,12,100,343,5,5};
vector<int> vec(ia,ia+7);//{ 123,343,12,100,343,5,5}
vector<int> vec1(2,4);//{4,4}
vector<int> vec2(4);{0,0,0,0}
输出vector中的所有数据
vector<int>::iterator itEnd = vec.end();
for(vector<int>::iterator it = vec.begin(); it != itEnd; ++it)
std::cout << *it << endl;
添加数据
vector<int>::iterator it(vec.rbegin().base());
vec.insert(it,1);
或
vector<int>::iterator it = vec.begin();
vec.insert(it,1);
list是链表的抽象数据结构(ADT)。list中的所有数据在空间分配上不一定是连续存放的。相对vector,list没有capaciy属性。
list中的迭代器的种类为双向存取迭代器(bidirectional -access-iterator)。
请看下面的例子:
构造list
int ia[] = {123,343,12,100,343,5,5};
list<int> ls;
list<int> ls(ia,ia+7); //{ 123,343,12,100,343,5,5}
list<int> ls(2,4); //{4,4}
list<int> ls(4);{ 0,0,0,0}
输出list中的所有数据
list<int>::iterator itEnd = ls.end();
for(list<int>::iterator it = ls.begin(); it != itEnd; ++it)
std::cout << *it << endl;
deque,即双向链表。相对vector,deque也是连续空间,但不是vector的连续线性空间。
deque中的迭代器的种类为随机存取迭代器(random-access-iterator)。
deque成员函数如下表:
函数
|
描述
|
c.assign(beg,end)
c.assign(n,elem)
|
将[beg; end)区间中的数据赋值给c。
将n个elem的拷贝赋值给c。
|
c.at(idx)
|
传回索引idx所指的数据,如果idx越界,抛出out_of_range。
|
c.back()
|
传回最后一个数据,不检查这个数据是否存在。
|
c.begin()
|
传回迭代器重的可一个数据。
|
c.clear()
|
移除容器中所有数据。
|
deque<Elem> c
deque<Elem> c1(c2)
deque<Elem> c(n)
deque<Elem> c(n, elem)
deque<Elem> c(beg,end)
c.~deque<Elem>()
|
创建一个空的deque。
复制一个deque。
创建一个deque,含有n个数据,数据均已缺省构造产生。
创建一个含有n个elem拷贝的deque。
创建一个以[beg;end)区间的deque。
销毁所有数据,释放内存。
|
c.empty()
|
判断容器是否为空。
|
c.end()
|
指向迭代器中的最后一个数据地址。
|
c.erase(pos)
c.erase(beg,end)
|
删除pos位置的数据,传回下一个数据的位置。
删除[beg,end)区间的数据,传回下一个数据的位置。
|
c.front()
|
传回地一个数据。
|
c.get_allocator
|
使用构造函数返回一个拷贝。
|
c.insert(pos,elem)
c.insert(pos,n,elem)
c.insert(pos,beg,end)
|
在pos位置插入一个elem拷贝,传回新数据位置。
在pos位置插入>n个elem数据。无返回值。
在pos位置插入在[beg,end)区间的数据。无返回值。
|
c.max_size()
|
返回容器中最大数据的数量。
|
c.pop_back()
|
删除最后一个数据。
|
c.pop_front()
|
删除头部数据。
|
c.push_back(elem)
|
在尾部加入一个数据。
|
c.push_front(elem)
|
在头部插入一个数据。
|
c.rbegin()
|
传回一个逆向队列的第一个数据。
|
c.rend()
|
传回一个逆向队列的最后一个数据的下一个位置。
|
c.resize(num)
|
重新指定队列的长度。
|
c.size()
|
返回容器中实际数据的个数。
|
C1.swap(c2)
|
将c1和c2元素互换。
|
关联容器(Associative Container)提供了根据key快速检索数据的能力。在关联容器(Associative Container)中,key和元素都是成对(pair)存在的,你可以调用std::make_pair使用key和元素值来构建一个pair。
STL提供的关联容器包括set、multiset、map、multimap。
set和map只支持唯一键(unique key),即对个key最多只保存一个元素。multiset和multimap则支持多个key,一个key可以对应多个元素。
set和map的区别在于,在set里面key和元素是同一个值,而在map里面key和元素分开存储。
set是集合的抽象数据结构(ADT)。不同于数学意义上的集合,STL中的set的所有的元素都是有序的而且set中所有的元素都是唯一的。
set中的迭代器的种类为双向存取迭代器(bidirectional-access-iterator)。
请看下面的例子:
构建一个set
set<string> fruits;
往set中添加数据
fruits.insert("apple");
fruits.insert("orange");
fruits.insert("banana");
输出set
set<string>::iterator itEnd = fruits.end();
for (set<string>::iterator it=fruits.begin(); it != itEnd; it++)
cout << *it << " ";
输出结果
apple banana orange
multiset和set基本相同,所不同的是,一个multiset中的元素是可以重复的。
multiset中的迭代器的种类为双向存取迭代器(bidirectional-access-iterator)。
multiset的操作同set。
请看下面的例子:
构建一个set
set<string> fruits;
往set中添加数据
fruits.insert("apple");
fruits.insert("orange");
fruits.insert("apple");
fruits.insert("banana");
fruits.insert("banana");
输出set
set<string>::iterator itEnd = fruits.end();
for (set<string>::iterator it=fruits.begin(); it != itEnd; it++)
cout << *it << " ";
输出结果
apple apple banana banana orange
map是字典的抽象数据结构(ADT)。map中的所有的元素都会根据key自动进行排序,而且所有的元素都是唯一的。map中的所有的元素都是pair,即键(key)和值(value)组成的序列(pair中的第一个元素为key,第二个元素为value)。
map中的迭代器的种类为双向存取迭代器(bidirectional-access-iterator)。
请看下面的例子:
构建一个map
typedef std::map<int,string> EMPLOYEE_MAP;
EMPLOYEE_MAP employees;
往map中添加数据
employees.insert(EMPLOYEE_MAP::value_type(25301, "A"));
employees.insert(EMPLOYEE_MAP::value_type(25302, "B"));
employees.insert(EMPLOYEE_MAP::value_type(25303, "C"));
employees.insert(EMPLOYEE_MAP::value_type(25304, "D"));
employees.insert(EMPLOYEE_MAP::value_type(25305, "E"));
输出map
EMPLOYEE_MAP::iterator itEnd = employees.end();
for (EMPLOYEE_MAP::iterator it = employees.begin(); it != itEnd; ++it)
{
std::cout << it->first << "-";
std::cout << it->second << endl;
}
输出结果
25301-A
25302-B
25303-C
25304-D
25305-E
multimap和map基本相同,所不同的是,一个multimap中的key可以重复。
multimap中的迭代器的种类为双向存取迭代器(bidirectional-access-iterator)。
multimap的操作同set。
这里要提到的就是hashtable,即哈希表的抽象数据结构(ADT)。
因为hashtable不是STL标准的一部分,部分编译器(如Microsoft Visual C++)并没有提供hashtable的实现。
hashtable中的迭代器的种类为前向迭代器(forward-iterator)。
在SGI的STL中,有hashtable的实现,其中的hash_set,hash_map,hash_multiset,
hash_multimap都是基于hashtable而构建的,是hashtable的适配器(Adaptor)。而在
Microsoft Visual C++的STL中,并没有提供STL的实现。其中的hash_set,hash_map,hash_multiset,hash_multimap都是基于STL中的hash算法而构建的。
hash_set的操作基本同set;
hash_map的操作基本同map;
hash_multiset的操作基本同multiset;
hash_multimap的操作基本同multimap。
STL中的算法以迭代器为参数,通过作用于迭代器而达到处理容器数据的目的。所有的算法都前两个参数都是以对迭代器(iterator),通常称为first和last。事实上,算法所处理的迭代器范围为[first,last),如果first==last则表明此范围为空。按照处理问题的不同,STL算法可分为四组,分别是:
1. 不改变序列的算法(None-mutating sequence Algorithm)
2. 改变序列的算法(Mutating sequence Algorithm)
3. 排序以及相关算法(Sorting and related Algorithm)
4. 常用数字算法(Generalized numeric Algorithm)
很多算法都有if版本,如remove_if,这类算法会根据判断条件进行相应的操作;有一些算法具有copy_if或copy版本,不同于非copy_if或copy版本的是,这些操作将获取的值改写另外的迭代器。
本文并不打算对这些算法作详细的介绍,只是对这些算法做简单性的描述。更多的内容,推荐参考《C++ Primer》或《STL和泛型程序设计》。
这类算法以输入迭代器(input-iterator)为输入参数,但不改写迭代器迭代器范围内的数据。典型的算法有:find、count、equal、search、for_each等。
这类算法以输入迭代器(input-iterator)为输入参数,往往还带有一个输出迭代器(output-iterator),对输入迭代器(input-iterator)处理的结果写入输出迭代器。典型的算法有:copy、transform、remove、rotate、reverse、fill、replace等。
这类算法往往以双向迭代器(bidirectional-iterator)为输入输出参数,对迭代器范围内的数据进行排序,最终序列里面的值都会发生改变。之所以把排序以及相关算法和”不改变序列的算法”分离出来是因为这类算法解决了是算法领域的一类问题,而且这类问题在算法领域的重要性异常突出。典型的算法有:sort、partial_sort、nth_element、qsort、stable_sort、partition、merge、inplace_merge等。
这类算法往往以双向迭代器(bidirectional-iterator)为输入输出参数,对迭代器范围内的数据进行数字操作(如内积等),最终序列里面的值都会发生改变。这类算法解决的是数学领域相关的问题,因此被划为一类。典型的算法有:next_permutation、pre_permutation、inner_product等。
适配器(Adaptor)是为了增强功能或/和添加约束,对容器、迭代器、算法重新进行了包装的模板类。设计模式中的Decorate模式的基本思想是实现适配器(Adaptor)的理论/实践基础。在STL中,适配器可分为三类:容器适配器、迭代器适配器以及算法适配器。
容器适配器是对特定的容器进行Decorate而形成的新的模板类。常见的容器适配器有:
1. vector适配器
stack:堆栈,是一种FILO的数据结构。
2. deque适配器
priority_queue:优先级队列,队列里面的所有数据都是按照优先级进行排序的。
queue:单向队列。
3. list适配器
slist:单向链表。
4. hashtable的适配器
hash_map,hash_set,hash_multimap,hash_multiset
迭代器适配器是对特定的迭代器进行Decorate而形成的新的模板类。常见的迭代器适配器有:
1. 反向迭代器(reverse iterator)
相对迭代器(iterator),反向迭代器(reverse iterator)是按照相反的顺序对容器的数据进行访问的,如
vector<int>::reverse_iterator ritBegin = vec.rbegin();
vector<int>::reverse_iterator ritEnd = vec.rend();
2. 插入迭代器(insert iterator)
插入迭代器简化了向容器中插入元素的工作,指定了向容器中插入元素的位置。STL中有三种插入迭代器:
n 前向插入迭代器
在容器的前面插入元素,如调用容器的push_front
n 后向插入迭代器
在容器的后面插入元素,如调用容器的push_back
n 任意插入迭代器
在容器的任意位置插入元素,如调用容器的insert
3. 原始存储迭代器(raw storage itertor)
允许算法将其结果保存到没有初始化的内存中。
函数对象适配器是对某类特定函数对象进行Decorate而形成的新的模板类。常见的迭代器适配器有:
1. 否定者 (negator)
STL中有两个否定者not1和not2,分别对一元和二元谓词(predicate)的执行结果取反。
2. 绑定者(Binder)
STL中有两个绑定者binder1st和binder2nd(你可以通过调用函数bind1st和bind2nd分别构建这两个绑定者),分别将特定值绑定到函数对象的第1个和第2个参数s。
3. 函数指针函数适配器(Adaptors for pointers to function)
因为STL中的算法一般都使用函数对象作为参数。如果需要使用原始的函数指针用于这些算法的输入参数,那么我们就需要将这些原始指针转化成特定的函数对象。STL提供了这样的转换函数,包括:
n 转化成员函数:mem_fun,mem_fun_ref,mem_fun1,mem_fun1_ref
n 转化非成员函数:ptr_fun
- 详细解说 STL 排序(Sort)
0 前言: STL,为什么你必须掌握 对于程序员来说,数据结构是必修的一门课.从查找到排序,从链表到二叉树,几乎所有的算法和原理都需要理解,理解不了也要死记硬背下来.幸运的是这些理论都已经比较成熟,算 ...
- STL标准模板库(简介)
标准模板库(STL,Standard Template Library)是C++标准库的重要组成部分,包含了诸多在计算机科学领域里所常见的基本数据结构和基本算法,为广大C++程序员提供了一个可扩展的应 ...
- STL的std::find和std::find_if
std::find是用来查找容器元素算法,但是它只能查找容器元素为基本数据类型,如果想要查找类类型,应该使用find_if. 小例子: #include "stdafx.h" #i ...
- STL: unordered_map 自定义键值使用
使用Windows下 RECT 类型做unordered_map 键值 1. Hash 函数 计算自定义类型的hash值. struct hash_RECT { size_t operator()(c ...
- C++ STL简述
前言 最近要找工作,免不得要有一番笔试,今年好像突然就都流行在线笔试了,真是搞的我一塌糊涂.有的公司呢,不支持Python,Java我也不会,C有些数据结构又有些复杂,所以是时候把STL再看一遍了-不 ...
- codevs 1285 二叉查找树STL基本用法
C++STL库的set就是一个二叉查找树,并且支持结构体. 在写结构体式的二叉查找树时,需要在结构体里面定义操作符 < ,因为需要比较. set经常会用到迭代器,这里说明一下迭代器:可以类似的把 ...
- STL bind1st bind2nd详解
STL bind1st bind2nd详解 先不要被吓到,其实这两个配接器很简单.首先,他们都在头文件<functional>中定义.其次,bind就是绑定的意思,而1st就代表fir ...
- STL sort 函数实现详解
作者:fengcc 原创作品 转载请注明出处 前几天阿里电话一面,被问到STL中sort函数的实现.以前没有仔细探究过,听人说是快速排序,于是回答说用快速排序实现的,但听电话另一端面试官的声音,感觉不 ...
- STL的使用
Vector:不定长数组 Vector是C++里的不定长数组,相比传统数组vector主要更灵活,便于节省空间,邻接表的实现等.而且它在STL中时间效率也很高效:几乎与数组不相上下. #include ...
- [C/C++] C/C++延伸学习系列之STL及Boost库概述
想要彻底搞懂C++是很难的,或许是不太现实的.但是不积硅步,无以至千里,所以抽时间来坚持学习一点,总结一点,多多锻炼几次,相信总有一天我们会变得"了解"C++. 1. C++标准库 ...
随机推荐
- 前端开发工程师 - 03.DOM编程艺术 - 第1章.基础篇(上)
第1章.基础篇(上) Abstract:文档树.节点操作.属性操作.样式操作.事件 DOM (Document Object Model) - 文档对象模型 以对象的方式来表示对应的html,它有一系 ...
- spark写入ES(动态模板)
使用es-hadoop插件,主要使用elasticsearch-spark-20_2.11-6.2.x.jar 官网:https://www.elastic.co/guide/en/elasticse ...
- 使用js跳转手机站url的若干注意点
引子: 去年年底公司开发手机站平台,经历了前期的用户群.市场调查,产品需求分析,产品原型设计,ui前端到程序开发上线测试等等工作,终于上线...此处略去本人作为前端开发的心情. 应该说,我们的手机站平 ...
- Quartz定时器原理与使用
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,是一个完全由java编写的开源作业调度框架. Quartz可以用来创建简单或为运行十个,百个,甚至是好几 ...
- 【第二章】Shell 变量
一.什么是变量? 变量就是一个固定的字符串(也可以是字符.数字的组合)代替更多.更复杂的内容,该内容可能是变量.路径.字符串等其他内容. 变量就是程序中保存用户数据的一块内存空间,而变量名就是这块内存 ...
- LogisticRegression Algorithm——机器学习(西瓜书)读书笔记
import numpy as np from sklearn.datasets import load_breast_cancer import sklearn.linear_model from ...
- lvs+keepalived详解
常用软件安装及使用目录 资源链接:https://pan.baidu.com/s/15rFjO-EnTOyiTM7YRkbxuA 网盘分享的文件在此 官网:http://www.linuxvir ...
- HADOOP docker(三):HDFS高可用实验
前言1.机器环境2.配置HA2.1 修改hdfs-site.xml2.2 设置core-site.xml3.配置手动HA3.1 关闭YARN.HDFS3.2 启动HDFS HA4.配置自动HA4. ...
- HADOOP docker(一):安装hadoop实验集群(略操蛋)
一.环境准备 1.1.机器规划 主机名 别名 IP 角色 9321a27a2b91 hadoop1 172.17.0.10 NN1 ZK RM 7c3a3c9cd595 hadoo ...
- C# 如何在winform中嵌入Excel,内嵌Excel,word
近使用.net做一个小软件遇到一个问题,就是想实现把excel表格在winform中打开,同时可以操作,不单单是打开.或者就提取数据.在网上找了好多资料,发现这方面的资料比较少,即使有,都是旧版本的使 ...