笔记:C++学习之旅---泛型算法
标准库并未给每个容器定义成员函数来实现这些操作,而是定义了一组泛型算法(generic algorithm):称他们为”算法“,是因为他们实现了一些经典算法的公共接口,如排序和搜索:称他们是“泛型的”,是因为它们可以用于不同类型的元素和多种容器类型(不仅包括标准库类型,如vector或list,还包括内置的数组类型),以及我们将看到的,还能用于其他类型的序列。
练习10.3,10.4
#include
<iostream>
#include
<vector>
#include
<numeric>
using
namespace
std;
int
main()
{
vector
<
int
> vec;
int
i = 0;
cout <<
"请输入整数"
<< endl;
while
(cin >> i)
{
if
(i == -1)
break
;
vec.push_back(i);
}
int
val;
cout <<
"请输入你要查找的数字\n"
;
cin >> val;
auto
result = find(vec.cbegin(), vec.cend(), val);
//查找输入的数字是否在容器中,在则显示the value x is present,否则显示the value is not present;
cout <<
"The value "
<< val << (result == vec.cend() ?
" is not present\n "
:
" is present\n"
) << endl;
//
cout <<
"accumulate1:"
<< accumulate(vec.cbegin(), vec.cend(), 0)<<endl;
//算出元素之和;
vector
<
double
> ivec = { 1.1, 2.2, 3.3, 4.4,5.5 };
cout <<
"accumulate2:"
<< accumulate(ivec.cbegin(), ivec.cend(), 0.0)<<endl; //16.5 前面是double,一定要注意用0.0
return
0;
}
关键概念:算法永远不会执行容器的操作
慎用内联
内联能提高函数的执行效率,为什么不把所有的函数都定义成内联函数?
如果所有的函数都是内联函数,还用得着“内联”这个关键字吗?
内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的
执行效率。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收
获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,
消耗更多的内存空间。以下情况不宜使用内联:
(1)如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
(2)如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。
类的构造函数和析构函数容易让人误解成使用内联更有效。要当心构造函数和析构
函数可能会隐藏一些行为,如“偷偷地”执行了基类或成员对象的构造函数和析构函数。
所以不要随便地将构造函数和析构函数的定义体放在类声明中。
一个好的编译器将会根据函数的定义体,自动地取消不值得的内联(这进一步说明
了inline 不应该出现在函数的声明中)。
C++ 语言支持函数内联,其目的是为了提高函数的执行效率(速度)。
重排容器元素的算法(sort)、消除重复单词(unique)、使用容器操作删除元素(erase)
练习:10.9
实现你自己的elimDups。测试你的程序,分别在读取输入后,调用unique后以及调用erase后打印vector。
#include
<iostream>
#include
<vector>
#include
<string>
#include
<algorithm>
using
namespace
std;
void
elimDups(
vector
<
string
> &
vec
)
{
sort(
vec
.begin(),
vec
.end());
//排序
auto
end_unique = unique(
vec
.begin(),
vec
.end());
//重排输入范围,使得每个单词只出现一次,找到重复元素;
cout <<
"unique after\n"
;
for
(
auto
i = 0; i !=
vec
.size(); ++i)
{
cout<<
vec
[i] <<
" "
;
}
cout<< endl;
vec
.erase(end_unique,
vec
.end());
//删除重复元素
cout <<
"erase after\n"
;
for
(
auto
i = 0; i !=
vec
.size(); ++i)
{
cout <<
vec
[i] <<
" "
;
}
cout << endl;
}
int
main()
{
string
word;
vector
<
string
> vec;
cout <<
"请输入一串单词(#结束)\n"
;
while
(cin >> word)
{
if
(word ==
"#"
)
break
;
vec.push_back(word);
}
elimDups(vec);
return
0;
}
lambda 表达式
练习10.16
使用lambda编写你自己版本的biggies.
#include
<iostream>
#include
<string>
#include
<vector>
#include
<algorithm>
using
namespace
std;
void
elimpDups(vector<string> &s)
{
sort(s.begin(), s.end());
//排序;
auto
new_end = unique(s.begin(), s.end());
//找出重复元素;
s.erase(new_end, s.end());
//删除重复元素;
}
void
biggies(vector<string> &words, vector<string>::size_type sz,ostream &os = cout,
char
c =
' '
)
{
elimpDups(words);
stable_sort(words.begin(), words.end(), [](string
const
&lhs, string
const
&rhs){
return
lhs.size() < rhs.size();});
//按长度排序,长度相同的单词维持字典序;
auto
wc = find_if(words.begin(), words.end(), [sz](string
const
&a){
return
a.size() >= sz;});
//找到满足size >= sz的元素数目;
/*for_each(wc, words.end(),[](const string &s){
cout << s << " ";});// 打印长度大于等于给定值的单词,每个单词后面接一个空格;
cout << endl;
*/
for_each(wc, words.end(), [&os, c](
const
string &s){
os << s << c;});
//加入引用捕获;ostream &os = cout,char c = ' ';
os << endl;
}
void
fun1()
{
size_t v1 = 42;
auto
f2 = [&v1]{
return
++v1; };
v1 = 0;
auto
j1 = f2();
cout << j1 << endl;
//j1 = 1;
}
void
fun2()
{
size_t v1 = 42;
auto
f2 = [v1]()
mutable
{
return
++v1; };
v1 = 0;
auto
j2 = f2();
cout << j2 << endl;
//j2 = 43
}
int
main()
{
vector<string> vec = {
"1234"
,
"1234"
,
"1234"
,
"hi~"
,
"alan"
,
"alan"
,
"cp"
,
"a"
,
"abc"
};
biggies(vec,3);
fun1();
//引用捕获
fun2();
//变量捕获
return
0;
}
默认情况下,对一个被拷贝的变量,lambda不会改变其值,如果我们希望能改变一个被捕获的变量的值,就必须在参数列表首加上关键字mutable。因此,可变的lambda能省略参数列表。
引用捕获
隐示捕获
谓词:
是一个可调用的表达式,其返回结果是一个能用做条件的的值。标准库算法所使用的的谓词分为两类:一元谓词(意味着它只接受单一参数)、二元谓词(意味着它们有两个参数)。
练习10.24:
给定一个string,使用bind和chaeck_size在一个int的vector中查找第一个大于string长度的值。
#include
<iostream>
#include
<vector>
#include
<algorithm>
#include
<string>
#include
<functional>
using
namespace
std;
inline
bool
check_size(
const
string
&
s
,
vector
<
int
>::
size_type
sz
)
{
return
s
.size() <
sz
;
}
inline
vector
<
int
>::
const_iterator
find_first_bigger(
const
vector
<
int
> &
vec
,
const
string
&
s
)
{
return
find_if(
vec
.cbegin(),
vec
.cend(),
bind(check_size,
s
, placeholders::_1));
}
int
main()
{
vector
<
int
> vec = { 1, 2, 3, 4, 5, 6 };
string
s = {
"test"
};
cout << *find_first_bigger(vec, s)<<endl;
//5,找到第一个比字符串“test“ 4 大的数.
return
0;
}
再探迭代器
除了为每个容器定义的迭代器之外,标准库在头文件iterator中还定义了额外几种迭代器:
插入迭代器:这些迭代器被绑定到一个容器上,可以来向容器插入元素。
流迭代器:这些迭代器被绑定到输入输出流上,可用来遍历所关联的IO流。
反向迭代器:这些迭代器向后面不是向前移动,除了forword_list之外的标准库容器都有反向迭代器。
移动迭代器:这些专用的迭代器不是拷贝其中的元素,而是移动它们。
插入迭代器
流迭代器