您的位置:首页 > 其它

STL源码剖析学习十三:算法之基本算法

2012-04-26 12:48 417 查看
先给个实例:

#include<iostream>
#include<numeric>
#include<vector>
#include<algorithm>
#include<functional>
#include<iterator>
#include<string>
using namespace std;
template <class T>
struct display
{
void operator()(const T& x) const
{
cout<<x<<" ";
}
};

int main()
{
int ia[9] = {0,1,2,3,4,5,6,7,8};
vector<int> iv1(ia, ia+5);
vector<int> iv2(ia, ia+9);
pair<vector<int>::iterator,vector<int>::iterator> p = mismatch(iv1.begin(), iv1.end(), iv2.begin());
if(p.first == iv1.end())
cout<<"iv1.end"<<endl;
else
cout<<*(p.first)<<endl;
if(p.second == iv2.end())
cout<<"iv2.end"<<endl;
else
cout<<*(p.second)<<endl;

cout<<equal(iv1.begin(), iv1.end(), iv2.begin())<<endl;
cout<<equal(iv1.begin(), iv1.end(), &ia[3])<<endl;
cout<<equal(iv1.begin(), iv1.end(), &ia[3], less<int>())<<endl;

fill(iv1.begin(), iv1.end(), 9);
for_each(iv1.begin(), iv1.end(), display<int>());
cout<<endl;
fill_n(iv1.begin(), 3, 7);
for_each(iv1.begin(), iv1.end(), display<int>());
cout<<endl;

vector<int>::iterator ite1 = iv1.begin();
vector<int>::iterator ite2 = ite1;
advance(ite2, 3);
iter_swap(ite1, ite2);
cout<<*ite1<<" "<<*ite2<<endl;
for_each(iv1.begin(), iv1.end(), display<int>());
cout<<endl;

cout<<max(*ite1, *ite2)<<endl;
cout<<min(*ite1, *ite2)<<endl;

swap(*iv1.begin(), *iv2.begin());
for_each(iv1.begin(), iv1.end(), display<int>());
cout<<endl;
for_each(iv2.begin(), iv2.end(), display<int>());
cout<<endl;

string stra1[] = {"jamie", "jjhou", "jason"};
string stra2[] = {"jamie", "jjhou", "jerry"};

cout<<lexicographical_compare(stra1, stra1+3, stra2, stra2+3)<<endl;
cout<<lexicographical_compare(stra1, stra1+3, stra2, stra2+3, greater<string>())<<endl;

fill_n(inserter(iv1, iv1.begin()), 20, 3);
for_each(iv1.begin(), iv1.end(), display<int>());
cout<<endl;

system("pause");
}




如果第二序列的元素比第一序列的元素大,则多余的部分忽略不计。
如果比第一序列的元素少,则会引发为定义行为

equal(first1, last1, first2)
{
for(; first1!=last1; ++first1, ++first2)
if(*first1 != *first2)
return false;
return true;
}


将区间内前n个元素改为新值,返回的迭代器指向被填入元素的最后一个元素的下一个位置

fill_n(first, n, value)
{
for(; n>0; --n,first)
{
*first = value;
}
return first;
}


如果n超越了容器现有的大小,会造成不可预期的行为,解决方法是把改写操作改成插入操作
fill_n(inserter(iv, iv.begin()), 5, 7);
从begin位置开始插入5个值为7的元素

将两个迭代器所指的对象对调

iter_swap(a, b)
{
__iter_swap(a, b, value_type(a));
}
__iter_swap(a, b, T*)
{
T tmp = *a;
*a = *b;
*b = tmp;
}


函数必须知道迭代器的value type 才能够声明一个对象tmp
设计双层构造,多了一个额外的参数value_type(a)
也可以写成:

iter_swap(ForwardIterator1 a, ForwardIterator2 b)
{
typename iterator_traits<ForwardIterator1>::value_type tmp = *a;
*a = *b;
*b = tmp;
}


以字典排列方式对两个容器内的元素进行比较

lexicographical_compare
{
for(; first1!=last1 && firs2!=last2; ++first1, ++first2)
{
if(*first1 < *first2)
return true;
if(*first2 < *first1)
return false;
}
return first1 == last1 && first2 != last2;
}


平行比较两个序列,找出第一个不匹配点,返回一对迭代器
如果第二个序列比第一个序列的元素多,多出来的忽略不计,如果少,则可能引发未定义的行为

mismatch(first1, last1, first2)
{
while(first!=last1 && *first1==*first2)
{
++first1;
++first2;
}
return pair<InputIterator1, InputIterator2>(first1, first2);
}


copy为了提高效率采用了各种方法,函数重载,型别特性,偏特化等
如果copy的输入区间和输出区间没有重叠,则没有问题,但是如果有重叠,则可能出现问题
只有当底层调用的函数为memmove时不会出现问题,因为他把所有区间先复制下来,不会出现区间被改写的问题

copy能为输出区间内的元素赋予新值,而不是产生新的元素,不能改变输出迭代器中的元素的个数。
如果需要向输出区间中插入元素,则需要用copy算法搭配insert_iterator

完全泛化版本:

copy(InputIterator first, InputIterator last, OutputIterator result)
{
return __copy_dispatch<InputIterator, OutputIterator>()(first, last, result);
}


重载函数:
针对原生指针,const char*和const wchar_t*,直接进行内存拷贝操作。

incline char* copy(const char* first, const char* last, char* result)
{
memmove(result, first, last-first);
return result+(last-first);
}


wchar_t版本同理

incline wchar_t* copy(const wchar_t* first, const wchar_t* last, wchar_t* result)
{
memmove(result, first, sizeof(wchar_t)*(last-first));
return result+(last-first);
}


__copy_dispatch有一个完全泛化版本和两个偏特化版本

template<class InputIterator, class OutputIterator>
struct __copy_dispatch
{
OutputIterator operator(InputIterator first, InputIterator last, OutputIterator result)
{
return __copy(first, last, result, iterator_category(first));
}
}


偏特化版本

template<class T>
struct __copy_dispatch<T*, T*>
{
T* operator(T* first, T* last, T* result)
{
typedef typename __type_traits<T>::has_trival_assignment_operator t;
return __copy(first, last, result, t());
}
}

template<class T>
struct __copy_dispatch<const T*, T*>


对于has_trival_assignment_operator不懂,下面是一种解释:
trivial的c++ class
就像是C语言里面的struct一样,就是自己没有虚函数表,自己的基类和成员都没有,
它的拷贝复制都只需要简单的逐位拷贝就行了
non-trivial class
是C++特有的,和C的struct有明显的区别,它的拷贝需要特殊处理,因为有虚函数表的存在

__copy_dispatch()根据迭代器的不同,调用不同的__copy()

InputIterator版本

__copy(InputIterator first, InputIterator last, OutputIterator result, input_iterator_tag)
{
for(; first!=last; ++result, ++first)
*result = *first;
return result;
}


RandomAccessIterator版本

__copy(RandomAccessIterator first, RandomAccessIterator last, OutputIterator result, random_access_iterator_tag)
{
return __copy_d(first, last, result, distance_type(first));
}


又划分出一个函数,为了重用

__copy_d(RandomAccessIterator first, RandomAccessIterator last, OutputIterator result, Distance*)
{
for(Distance n = last-first; n>0; --n,++result,++first)
*result = *first;
return result;
}


trival_assignment_operator 平凡的赋值操作符
如果指针所指对象拥有non_trival_assignment_operator,复制操作必须通过它来进行
但是如果指针所指对象拥有的是trival_assignment_operator
复制操作可以不通过它,直接用内存拷贝方式进行就可以
C++本身不能探测到某个对象是否拥有trival_assignment_operator,
可以通过__type_traits<>方法来弥补

通过增加间接层的方法对__copy_dispatch进行偏特化,衍生出两个__copy_t()

所指对象拥有trival_assignment_operator

template<class T>
incline T* __copy_t(const T* first, const T* last, T* result, __true_type)
{
memmove(result, first, sizeof(T),*(last-first));
return result+(last-first);
}


所指对象拥有non_trival_assignment_operator

template<class T>
incline T* __copy_t(const T* first, const T* last, T* result, __false_type)
{
return __copy_d(first, last, result, (ptrdiff_f*)0);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: