第三章 泛型编程风格

第三章 泛型编程风格

Standard Template Library(STL)两种组件构成:
容器(container)(vector、list、set、map...)
操作以上容器的所谓的泛型算法(generic algorithm)(find(), sort(), replace(), merge()...)

序列式容器(sequential container):vector、list
依次维护第一个元素、第二个元算......直到最后一个元素,进行迭代操作(iterate)
关联式容器(associative container):map、set
可以快速寻找到容器中的元素值

map:一对对的 key/value 组合。key 用于搜寻,value 用来表示我们要存储或取出的数据。
set:仅含有 key。我们对它进行查询操作,为的是要判断某值是否在于其中。

泛型算法:提供了许多可施行于容器类及数组类型上的操作行为。和想要操作的元素类型无关。
泛型算法通过 function template 技术,达成“与操作对象类型相互独立”的目的,而达成“与容器无关”的诀窍,就是不要直接在容器身上进行操作。取代方式是:藉由一对 iterators (first, last),标示我们想进行迭代的元素区间。每次first前进一个位置。

3.1 指针的算术运算(The Arithmetic of Pointers)

array[ 2 ] 等同于 *(array + 2) 地址运算中+2会把指针所指向的类型的容量大小算进去

array定义不可能为空,vector定义可以为空,所以要判断vector是不是为空

list:双向循环链表

见代码:

#include <iostream>
#include <vector>
#include <string>
//#include <ostream>

using namespace std;
/*
const int* find( const vector<int> &vec, int value){
    for (int i = 0; i < vec.size(); ++i)
        if ( vec[ i ] == value )
            return &vec [ i ];
    return 0;
}

template <typename elemType>
const elemType* find (const vector<elemType> &vec, elemType value){
    for (int i = 0; i < vec.size(); ++i)
        if ( vec[ i ] == value )
            return &vec [ i ];
    return 0;
}*/

//传入大小范围
template <typename elemType>
const elemType* find ( const elemType *array, int size, const elemType &value);

//传入读取操作的终点
template <typename elemType>
const elemType* find ( const elemType *array, const elemType *sentinel, const elemType &value);

template <typename elemType>
const elemType* find ( const elemType *array, int size, const elemType &value){
    cout << "\tWelcome to elemType* find ( const *, int size, const &)" << endl;
    if ( ! array || size < 1)
        return 0;
    for ( int i = 0; i < size; ++i)
        if ( array[ i ] == value )//*(array+i)
            return &array[ i ];
/*    for ( int i = 0; i < size; ++i, ++array )
        if ( *array == value )
            return array;*/
    return 0;
}

template <typename elemType>
const elemType* find ( const elemType *first, const elemType *last, const elemType &value){
    cout << "\tWelcome to elemType* find ( const *, const *, const &)" << endl;
    if ( ! first || ! last )
        return 0;
    for ( ; first != last; ++first )
        if ( *first == value)
            return first;
    return 0;
}

//获取第一个元素的地址
template <typename elemType>
inline const elemType* begin( const vector<elemType> &vec )
    { return vec.empty() ? 0 : &vec[0]; }
//获取最后一个元素的地址
template <typename elemType>
inline const elemType* end(  const vector<elemType> &vec )
    { return vec.empty() ? 0 : &vec[vec.size()]; }

template <typename elemType>
void display( const vector<elemType> &vec, ostream &os = cout){
    os << "display template\n";
    vector<elemType>::const_iterator iter = vec.begin();
    vector<elemType>::const_iterator end_it = vec.end();
    for( ; iter != end_it; ++iter )
        os << *iter << ' ';
    os << endl;
}


int main()
{
    int a[]={1,2,3,4,5,6,7,8,9,523};
    string str_test[4] = {"pooh", "piglet", "Rodge", "test" };
    vector<int> test(a, a+10);
    vector<int> testnull;
    const vector<string> cs_vec(str_test, str_test+4);
    vector<string>::const_iterator iter_test = cs_vec.begin();
    cout << iter_test->size() << endl;
    /*for (int i =0; i <test.size(); i++)
        cout << test.at(i) << " ";*/
    for ( vector<int>::iterator iter = test.begin();
                            iter != test.end(); ++iter )
        cout << *iter << " ";
    cout << endl;
    display( test );
    //if( find(test, 523) ) cout << "find--" <<*(find(test, 523))<< endl;
    //else cout << "not find" << endl;

    int ia[8] = {1, 2, 3, 523, 4, 5, 6, 7};
    double da[6] = {1.5, 2.4, 5.4, 5.23, 8.8, 5.5};
    string sa[4] = {"pooh", "piglet", "Rodge", "test" };

    const int *pi = find( ia, ia+8, ia[3] );
    const double *pd = find( da, da+6, da[3] );
    const string *ps = find( sa, sa+4, sa[2] );

    pi = find (ia, 8, ia[3]);

    cout <<*pi<<endl<<*pd<<endl<<*ps<<endl;

    if( ! test.empty() ){
        cout <<*( find( begin( test ), end( test ), 523 ) ) << endl;
    }
    ///cout << *( find( begin( testnull), end( testnull), 523 ) ) << endl;
    if( find( begin( testnull), end( testnull), 523 ) )
        cout << "find" <<endl;
    else cout << " Not find " <<endl;
    return 0;
}


3.2 了解Iterators(泛型指针)

每个标准容器都提供一个名为begin() 的操作函数,可返回一个iterator,指向第一个元素。
另一个名为end() 的操作函数会返回一个 iterator,指向最后一个元素的下一位置。

嵌套(nested)

vector<string> svec;
vector<string>::iterators iter = svec.begin();

对于const vector<string> cs_vec;
用vector<string>::const_iterator iter = cs_vec.begin();

vector 用iterator迭代器操作,和下标操作一样,现代C++倾向于使用迭代器操作。
const_iterator,该类型只能访问容器内元素,但不能改变其值。
vector不是一种数据类型,而只是一个类模板,可用来定义任意多种数据类型

//iterators
template <typename IteratorType, typename elemType>
IteratorType find ( IteratorType first, IteratorType last, const elemType &value ){
    cout << "IteratorType\n";                                                                        //cout
    for (; first != last; ++first )
        if ( value == *first )
            return first;
    return last;
}

int main()
{
    const int asize = 8;
    int ia[ asize ] = { 1, 2, 3, 5, 8, 13, 21, 523 };

    // 以ia的8个元素作为list 和 vector 的初值
    vector<int> ivec( ia, ia+asize );
    list<int> ilist( ia, ia+asize );

    int *pia = find( ia, ia+asize, 1024 );
    if (pia != ia+asize )
        cout << "vector--Found! Congratulation!\n";
    else cout << "vector--Not Found! Bad!\n";

    vector< int >::iterator it;
    it = find( ivec.begin(), ivec.end(), 523 );
    if ( it != ivec.end() )
        cout << "iterator--vector--Found! Congratulation!\n";
    else cout << "iterator--vector--Not Found! Bad!\n";

    list< int >::iterator iter;
    iter = find( ilist.begin(), ilist.end(), 1024 );
    if ( iter != ilist.end() )
        cout << "iterator--list--Found! Congratulation!\n";
    else cout << "iterator--list--Not Found! Bad!\n";

    /*vector<string> guake;
    guake.push_back( "挂科吧" );
    for (vector<string>::iterator bigua =guake.begin(); bigua != guake.end(); )
        cout << *bigua ;*/
    return 0;
}

使用了底部元素所属类型的equality(相等)运算符?

标准程序库里面提供了find_if(),能够接受函数指针或function object,取代底部元素的equality 运算符,大大提升弹性。

总共超过60个的泛型算法(事实上近75个)(什么是近。。。)

搜寻算法(search algorithm):find(),count(),adjacent_find(),find_if(),count_if(),binary_search(),find_fisrt_of()
排序(sorting)及次序整理(ordering)算法:merge(),partial_sort(),partition(),random_shufflel(),reverse(),rotate(),sort()
复制(copy)、删除(deletion)、替换(substitution)算法:copy(),remove(),remove_if(),replace(),replace_if(),swap(),unique()
关系(relational)算法:equal(),includes(),mismatch()
数值(numeric)算法:accmulate(),adjacent_difference(),partial_sum(),inner_product()
集合(Set)算法:set_union(),set_difference()

3.3 所有容器的共同操作

equality(==)和inequality(!=)运算符,返回true或false
assignment(=)运算符,将某个容器复制给另一个容器
empty() 会在容器无任何元素时返回 true,否则返回 false,记住:无元素返回 trueif(vet.empty())为真,说明没有元素
size() 返回容器内当前含有的元素数目
clear() 删除所有元素

每个容器都有 begin() 和 end() 两个函数
begin()    返回一个 iterator,指向容器的第一个元素
end()    返回一个 iterator,指向容器的最有一个元素的下一个位置

insert()    将单一或某个范围内的元素安插到容器内
erase()    将容器内的单一元素或某个范围内的元素删除
insert() 和 erase() 的行为视容器本身为循序式(sequential)或关联式(associative)而有所不同。

3.4 使用序列式容器(Sequential Containers)

序列式容器用来维护一组排列有序、类型相同的元素,其中有第一、第二。。。以此类推,乃至最后一个元素。
vector 和 list 是两个最主要的序列式容器。
vector 以一块连续内存来存放元素
所以:任意位置插入、删除元素的效率很低,因为涉及到大批的元素移动;随机存取,颇有效率
适合:数列

list 以双向链接(double-linked,而非连续内存)来存储内容,可以执行前进或后退操作。
list 中的每个元素都包含 3 个字段:value、back指针(指向前一个元素)、front指针(指向下一个元素)
所以:任意位置插入、删除,效率很高;如果随机存取操作,效率不佳
适合:从文件中读取分数,希望由低到高排序存储,每读入一个分数,需要随机插入容器中
( If we were reading test scores from a file and wished to store
each score in ascending order, we are likely to be randomly inserting into the container with each
score we read. In this case, the list container is preferred.)

deque(读 deck) deque 的行为和 vector 颇为想死,都是连续内存存储元素,但deque 对于最前端元素的安插和删除操作效率更高;末端元素异同
标准程序库的 queue 便是以 deque 实现完成的,也就是说,以 deque 作为!底部!,存储元素

头文件:

#include <vector>
#include <list>
#include <deque>

1)定义空容器:

list< string > slist;
vector< int > ivec;

2)定义带大小的容器:

list< int > ilist( 1024 );
vector< string > svec( 32 );

3)定义特定大小并为每个元素指定初值:

vector< int > ivec( 10, -1 );
list< string > slist(16, "Hello" );

4)通过一对 Iterators 产生容器,这对 Iterators 用来标示一整组作为初值的元素区间

int ia[ 8 ] = {1, 1, 2, 3, 5, 8, 13, 21};
vector< int > fib ( ia, ia+8 );

5)根据某个容器产生出新容器,复制原容器内的元素,作为新容器的初值

list< string > slist;
// 填充 slist....
list< string > slist2( slist );    //将 slist 复制给 slist2

push_back() 在最末端安插一个元素
pop_back() 删除最后一个元素
list 和 deque 提供(不包括 vector )还提供了 push_front() 以及 pop_front()
pop_back() 和 pop_front() 这两个操作函数不会返回被删除的元素值。

读取元素:
front() 读取最前端元素的值
back() 读取最某端元素的值

push_front() 和 push_back() 属于特殊化的安插(insertion)操作
通用安插函数insert() 的四种变形:
• iterator insert( iterator position, elemType value )可将 value 安插到 position 之前,它会返回一个 iterator,指向被安插的元素

list<int> ilist;
// ... fill up ilist

list<int>::iterator it = ilist.begin();
while (it != ilist.end())
    if (*it >= ival){
        ilist.insert(it, ival);
        break; // exit loop
    }
if (it == ilist.end())
    ilist.push_back(ival);

• void insert( iterator position, int count, elemType value )可将 position 之前安插 count 个元素,这些元素的值都和 value 相同

string sval("Part Two");
list<string> slist;
// ... fill slist ...

list<string>::iterator it = find(slist.begin(), slist.end(), sval);
slist.insert(it, 8, string("dummy"));

• void insert( iterator1 position, iterator2 first, iterator2 last ) 可在 position 之前安插 [first, last] 所标示的各个元素

int ia1[7] = { 1, 1, 2, 3, 5, 55, 89 };
int ia2[4] = { 8, 13, 21, 34 };
list<int> elems(ia1, ia1+7);
list<int>::iterator    it = find(elems.begin(), elems.end(), 55);
elems.insert(it, ia2, ia2 + 4);

• iterator insert( iterator position ) 可在 position 之前插入元素,此元素的初值为其所属类型的默认值

pop_front() 和 pop_back() 皆属于特殊化的抹除(erase)操作。
通用的抹除函数 erase() 的两种变形:
• iterator erase (iterator posit ) 可抹除 posit 所指的元素。

list<string>::iterator it = find(slist.begin(), slist.end(), str);
slist.erase(it);

• iterator erase( iterator first, iterator last ) 可抹除 [first, last] 范围内的元素

list<string>::iterator
    first = slist.begin(),
    last = slist.end();
    
// it1: first element to erase,
// it2: first element beyond elements to erase
list<string>::iterator it1 = find(first, last, str);
list<string>::iterator it2 = find(first, last, sval);

slist.erase(it1, it2);

list 不支持偏移运算,不能用 slist.erase( it1, it1+num_tries ); ,所以用find

3.5 使用泛型算法

头文件

#includes <algorithm>

1.find() 用于搜寻无序(unordered)集合中是否存在某值。搜寻范围由Iterators[first, last]标出。如果找到目标,find() 会返回一个 iterator 指向该值,否则返回一个 iterator 指向 last
2.binary_search() 用于已序(sorted)集合的搜寻。如果如果搜寻到目标,就返回 true,否则返回false。binary_search() 比 find() 更有效率
find()属于 linear search,效率较 binary search差
linear search:one item at a time, without jumping.  O(n)
binary search:when you start with the middle of a sorted list, and see whether that's greater than or less than the value you're looking for, which determines whether the value is in the first or second half of the list. Jump to the half way through the sublist, and compare again etc.  O(log n)
3.cout() 返回数值相等的元素数目
4.search() 比较某个容器内是否存在某个子序列。如果搜到,则 search() 会返回一个 iterator,指向子序列起始处。如果不存在,就返回一个 iterator 指向容器末尾

由于我们的 vector 必定以递增顺序存储其值,因此,binary_search() 是我们的最佳选择。

max_element():Returns an iterator pointing to the element with the largest value in the range [first,last)

vector 元素系递增排列,所以 int max_value = vec.empty() ? 0 : vec[vec.size()-1];//????,人为的排好序了。。
调用binary_search() 之前,确保其实施对象必须经过排序,程序员自行排序。。

vector<int> temp( vec.size() );
copy( vec.begin(), vec.end(), temp.begin() );//确保“目标容器”空间足够,或者使用 inserter,安插模式取代默认的assignment行为
sort( temp.begin(), temp.end() );
return binary_search( temp.begin(), temp.end(), elem );

3.6 如何设计一个泛型算法

不对任何元素进行两次以上的检视:

int count_occurs(const vector<int> &vec, int val){
    vector<int>::const_iterator iter = vec.begin();
    int occurs_count = 0;
    while ((iter = find(iter, vec.end(), val)) != vec.end()){
        ++occurs_count;
        ++iter; // address next element
    }
    return ocurs_count;
}

Function Objects
所谓function objects,是某种 class 的实体对象,这类 classes 对 function call 运算符进行了重载操作,这样一来,可使 funct object 被当成一般函数来使用。
为什么:效率!可以令 call 运算符称为 inline ,因为消除“通过函数指针来调用函数”时需付出的额外代价。

标准程序事先定义了一组 function objects,分为算术运算(arithmetic)、关系(relational)、逻辑运算(logical)三大类
6 个算术运算:plus<type>, minus<type>, negate<type>, multiplies<type>, divides<type>, modules<type>
6 个关系:less<type>, less_equal<type>, greate<type>, greate_equal<type>, equal_to<type>, not_equal_to<type>
3 个逻辑运算:分别对应 &&, ||, ! 运算符:logical_and<type>, logical_or<type>, logical_not<type>
头文件:#include <functional>

如果 sort() 会使用底部元素的类型所供应的 less-than 运算符,将元素递增排序。

sort( vec.begin(), vec.end(), greater<int>() );//递减方式排序,greater<int>() 会产生一个匿名的 class template object 传给sort()

Function Object Adapters

adapter(配接器)

binder adapter(绑定配接器)

标准程序库提供了两个 binder adapter:bind1st 会将指定值绑定至第一操作数,bind2nd 则将指定值绑定至第二操作数。

bind2nd( less<int>, val );//会把 val 绑定于 less<int> 的第二个元素身上,于是,less<int> 会将每个元素拿来和 val 比较

消除 filter 和 vector 的关联性,使 filter() 更加泛型化

另一种 adaptor 是所谓的 negator,它会逆转 function object 的真伪值,not1 可逆转 unary function object 的真伪值,not2 可逆转 binary function object 的真伪值。

not1( bind2nd( less<int>, 10 ))//找出所有大于或等于10的元素

找出小于指定值的所有元素:
方法1:依次遍历每个元素,找出小于某值的所有元素
方法2:对vector排序,然后 find_if() 找出第一个指定值的元素位置,然后在删除该位置之后至 vector 末尾的所有元素。

sort() 默认从小到大排序

3.7 使用Map

map 被定义为一对(pair)数值,其中的 key 通常是个字符串,扮演索引的角色,另一个数值是 value。
字典便是 map 的一个不错的实体。

string 不接收空格,出现空格、tab键、回车视为输入结束

map 也有 begin() 和 end() ,map 对象有一个名为 first 的member,对应于 key,另一个名为 second 的 member,对应于 value
it->first

查询 map 中是否存在某个 key:
1、把 key 当索引使用 int count = words[ "vermeer" ];
当用来索引的那个 key 不存在于 map 中,则那个 key 会自动被加入 map 中
2、利用 map 的 find() 函数,返回一个 iterator,指向 key/value 形成的一个 pair(pair class 是标准程序库的一员),反之返回 end()

map<stirng,int>::iterator it;
it = words.find( "hello" );
if ( it != words.end() )
    cout << it->second;

3、利用 map 的 count() 函数,count() 会返回某特定项目在 map 内的个数:

int num = 0;
if ( words.count( "hello" ) )//exist
    num = words[ "hello" ];

任何一个 key 值在 map 内最多只会有一份。如果我们需要存储多份相同的 key 值,就必须使用 multimap。

3.8 使用 Set

set 由一群 keys 组合而成。
对于任何 key 值,set 只能存储一份。如果要存储多份相同的 key 值,必须使用 multiset。

int ia[ 10 ] = { 1, 5, 8, 3, 5, 3, 1, 5, 8, 523 };
vector< int > vec( ia, ia+10 );
set<int> iset( vec.begin(), vec.end() );
for (set<int>::iterator iter = iset.begin(); iter != iset.end(); ++iter )
    cout << *iter << ' ';
    //输出为:1 3 5 8 523

插入单个元素:insert()
insert() 是重载函数,可传递单一参数或双参数

和 set 相关的泛型算法:set_intersection()、set_union()、set_difference()、set_symmetric_difference()

3.9 如何使用 Iterator Inserters

vector 是动态的,为什么还要考虑容量的问题???

“会对元素进行复制行为”的泛型算法:
copy(), copy_backwards(), remove_copy(), replace_copy(), unique_copy(),都和 filter() 的实现极为相似

标准程序库提供了3个所谓的insertion adapters,这些 adapter 让我们得以避免使用容器的 assignment 运算符。
• back_inserter() 会以容器的 push_back() 函数取代 assignment 运算符。
• inserter() 会以容器的 insert() 函数取代 assignment 运算符,inserter() 接受两个参数:一个是容器,一个是 iteraotor,指向容器内的安插操作起始点。
• front_inserter() 会以容器的 push_front() 函数取代 assignment 运算符。这个 inserter 只适用于 list 和 deque。

想要使用上述3种 adapters,首先必须含入 iterator 头文件:

#include <iterator>

3.10 使用 iostream Iterators

标准程序库定义有供输入及输出用的 iostream iterator 类,称为 istream_iterator 和 ostream_iterator,分别支持单一类型的元素读取和写入。
头文件:#include <iterator>

通常希望从文件中读取,写到文件中去。
将 istream_iterator 绑定至 ifstream object,将 ostream_iterator 绑定至 ofstream object。

#include <iostream>
#include <fstream>
#include <iterator>
#include <algorithm>
#include <vector>
#include <string>
using namespace std;
int main()
{
    ifstream in_file("as_you_like_it.txt");
    ofstream out_file("as_you_like_it_sorted.txt");
    if (! in_file || ! out_file){
        cerr << "!!unable to open the necessary files.\n";
        return -1;
    }
    istream_iterator<string> is(in_file);
    istream_iterator<string> eof;
    vector<string> text;
    copy(is, eof, back_inserter(text));
    sort(text.begin(), text.end());
    ostream_iterator<string> os(out_file, " ");
    copy(text.begin(), text.end(), os);
}

 

还有好多东西需要学习,需要严肃的对待这些问题

转载于:https://www.cnblogs.com/Rodge/p/5143894.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值