笔记:C++学习之旅---泛型算法

2023-07-29,,

       标准库并未给每个容器定义成员函数来实现这些操作,而是定义了一组泛型算法(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之外的标准库容器都有反向迭代器。

移动迭代器:这些专用的迭代器不是拷贝其中的元素,而是移动它们。

插入迭代器

流迭代器

笔记:C++学习之旅---泛型算法的相关教程结束。

《笔记:C++学习之旅---泛型算法.doc》

下载本文的Word格式文档,以方便收藏与打印。