嵌入式c++软件开发笔记第五讲

类型推导
意义
理解编译器类型推导规则有利于高效的使用C++
从明显或冗余的类型拼写中解放出来。这样使得c++软件更具有适配性
类型推导的场景
调用模板函数
使用auto
decltype表达式
神秘的decltype(auto)
万能引用(未定义引用)
C++11提出万能引用的原因–能够接受左值和右值
定义:templatevoid func(T&& param){}
万能引用语境:
必须是函数模板
必须发生了模板类型推断
辨识万能引用
类型参数T必须紧跟&&
void MyFuncTemp2(std::vector&& v1)//并不是万能引用
类模板里的成员函数形参为T&&,并不是万能引用
剥夺万能引用
const属性会剥夺万能引用:templatevoid func(const T&& param){}
总结
万能引用并不是一种新的类型,只存在函数模板中;
函数模板的类型推导原则
ParamType是一个指针或引用 ,但不是万能引用
若实参类型expr是引用,则引用类型会被忽略,保持cv属性;T不会被推导为引用类型
template
void func(T& param);

int x = 27;
const int cx = x;
const int& rx = x;

f(x); // x为int,因此param的类型为int&而T为int
f(cx); // cx为const int,因此param的类型为const int&而T为const int
f(rx); // rx为const int&,忽略引用后为const int,因此param的类型为const int&而T为const int

ParamType是一个万能引用
如果expr是一个左值,T和ParamType都被推导成左值引用
如果expr是一个右值,T是由expr的类型决定、ParamType被推导成右值引用。
引用折叠、转发、完美转发
引用折叠
折叠场景:ParamType是一个万能引用,实参expr是一个左引用或者右引用
折叠规则
所有右值折叠到右值引用上仍然是一个右值引用。(A&& && 变成 A&&)
所有的其他引用类型之间的折叠都将变成左值引用。 (只要有左值引用就是左值引用)(A& & 变成 A&; A& && 变成 A&; A&& & 变成 A&)
转发
把函数模板将收到的参数及参数类型不变的传递给其他函数,叫做转发
若函数模板里的调用的函数形参为右值引用,该如何传递参数?
方法1:使用std::move
方法2:使用完美转发std::forward
template
void func(T&& param);

int x = 27;
const int& rx = x;

f(x); // 由于x为左值,因此T和param都被推导为int& – ①

f(rx); // 由于rx为左值,因此T和param都被推导为const int& – ①

f(27); // 由于27是右值,其类型为int,与T&&进行对比得到T类型为int而param类型为int&& – ②

ParamType不是指针或引用(值传递)
如果expr是一个引用,则忽略引用部分
如果expr是个const或者volatile,也要忽略掉const或volatile
template
void func(T param);

int x = 27;
const int cx = x;
const int& rx = x;

f(x); // x类型为int,因此T和param的类型也为int
f(cx); // cx类型为const int,忽略掉const,因此T和param的类型仍为int
f(rx); // rx类型为const int&,忽略掉const和&,因此T和param的类型还是int

模板类型推导时,数组和函数参数会退化为指针。
void someFunc(int, double);

template
void func1(T param);

template
void func2(T& param);

func1(someFunc); // param被推导为函数指针,类型为void(*)(int, double)
func2(someFunc); // param被推导为函数指针,类型为void(&)(int, double)

auto类型推导
原则:
当不声明为指针或引用时,auto 的推导结果和初始化表达式抛弃引用和 cv(const 和 volatile 限定符的统称) 限定符后类型一致。
指针:auto 的推导结果将保持初始化表达式的 cv 属性
引用:忽略引用,忽略cv属性
auto限制
auto不能用于函数参数
auto不能用于非静态成员变量
auto无法定义数组
auto无法推导出模板参数
特殊推导规则
推导的类型是std::initializer_list
auto x = {1, 2, 3} // x的类型为std::initializer_list
decltype
作用
decltype关键字返回给定变量或表达式的类型,并且可以用这个返回的类型来声明变量。
注:decltype不会真正计算表达式的值或者调用函数
推导规则
decltype(类型)
变量–变量的类型(引用属性和const属性保留)
引用、指针–指针推断指针,引用推断引用—解指针decltype推断出引用;
表达式–注:表达式如果可以做左值,则返回左值引用,
函数—返回值的类型
主要用途
问题–链接
解决办法–链接
神秘的decltype(auto)-C++14
应用于函数返回值类型推导
auto VS decltype
1.auto忽略顶层const,decltype保留顶层const;
2.引用操作,auto推断出原有类型,decltype推断出引用;
3.解引用操作,auto推断出原有类型,decltype推断出引用;
4.auto推断时会实际执行,decltype不会执行,只做分析
可调用对象:通过函数调用符操作的对象称之为可调用对象
可调用对象
普通函数
类成员函数/静态函数
函数指针
类的成员函数指针
可转换为函数指针的类
函数对象(仿函数)
lambda表达式
本质
实际是匿名函数,能够捕获一定范围的变量;
与普通函数不同,可以在函数内部定义;
格式及说明
捕获列表->返回类型{函数体};
说明:
返回类型可以由编译器推导出,所以可以省略;
参数可以有默认值
C++14的lambda形参可以使用auto声明
没有参数时,()也可以省略
lambda调用方法和普通函数一样
捕获列表
作用:捕获外部作用域一定范围的变量
[ ]:不捕获任何变量,但不包括静态局部变量,lambda表达式可以直接使用
[&]:捕获外部作用域中所有变量,并作为引用在函数体里使用;
[=]:捕获外部作用域中所有变量,并作为副本(传值)在函数体里使用;但不能修改外部作用域变量的值;
[this]:一般用于类中,捕获当前类中的this指针,让lambda表达式与成员函数具有一样的权限,访问成员变量;(若使用[&]/[=],默认捕获this)
[变量名]:捕获列表中列出的变量,多个变量用,分隔
[=,&变量名]:按值捕获外部变量,按引用捕获指定变量
[&,变量名]:按引用捕获外部变量,按值捕获指定变量
lambda表达式延迟调用注意事项
切记不能发生“引用悬挂”–即按引用捕获会导致包含指向局部变量的引用,当lambda离开局部作用域时,会导致引用的那个变量被释放,lambda里的那个引用发生"悬挂"
this陷阱–定义局部变量
void print()
{
//int x = m_x;
//int y = m_y;
s_print_history.push_back(x1 = m_x, x2 = m_y{std::cout << “(X:” << x1<< “,Y:” << x2<<")" << std::endl;});
std::cout << “(X:” << m_x << “,Y:” << m_y <<")" << std::endl;
}

尽量避免使用复杂的lambda
lambda里避免有全局变量或静态变量或者比当前类生命周期更长的变量
std::function包装器
本质–
一个类模板,用于包装可调用对象。可以容纳除了类成员(函数)指针之外的所有可调用对象
作用
可以用统一的方式来保存或传递可调用对象。
意义
实现了一套类型消除机制,可用统一的方式处理不同类型的可调用对象。
std::function进一步深化以数据为中心(封装)的面向对象思想(连函数都对象化了)
案例–#include
链接
std::bind适配器
本质
std::bind也是一个函数模板,返回值是一个仿函数,也是可调用对象
作用
将多元的可调用对象与其参数一起绑定成一个仿函数对象。
将多元(设参数个数为n)的可调用对象转成一元或(n-1)元的可调用对象,即只绑定部分参数。
bind可以绑定的对象
普通函数(functions)
函数对象(仿函数,function objects)
类的成员函数(member functions。注意:_1必须是某个对象的地址)
类的数据成员(data members注意:_1必须是某个对象的地址)
使用案例
链接
注意:bind预先绑定的参数需要传具体的变量或值进去,对于预先绑定的参数是按值传递的。
可变参数
initializer_list 列表初始化
本质:用于表示某种特定类型的值的数组,和vector一样,initializer_list也是一种模板类型。
使用:使用一个花括号来初始化变量e.g.:std::vectora{1,2,3,4,5};
初始化区别:int c = 3.3; //这里会进行默认类型转换
int b = {3.3}; //这里编译器会给出警告(也有可能是错误)
优点:代码更加健壮
C++11允许构造函数和其他函数把初始化列表当做参数
#include
#include

class MyNumber
{
public:
MyNumber(const std::initializer_list &v) {
for (auto itm : v) {
mVec.push_back(itm);
}
}

void print() {
for (auto itm : mVec) {
    std::cout << itm << " ";
}

}
private:
std::vector mVec;
};

int main()
{
MyNumber m = { 1, 2, 3, 4 };
m.print(); // 1 2 3 4

return 0;

}

萃取技术
作用:提取一些信息出来
萃取接口:https://en.cppreference.com/w/cpp/types
案例–链接
C++ STL/boost
STL概述
STL六大组件
Container(容器) 各种基本数据结构
Iterator(迭代器) 连接containers和algorithms
Algorithm(算法) 各种基本算法如sort、search…等
Adapter(适配器) 可改变containers、Iterators或Function object接口的一种组件
Function object(函数对象)
Allocator(分配器)
STL六大组件关系

容器
分类
顺序容器(sequence Containers)
放进去在哪里,这个元素就排在哪里。比如array,vector,deque,list,forward_list;
关联容器(Associative Containers)
树,哈希表,元素是 键/值 对 ,特别适合做查找。你能控制插入的内容,但一般来讲你不能控制插入的位置。set,multiset,map,multimap;
无序(散列)容器(Unordered Containers)
哈希表;c++11里推出:元素的位置不重要,重要的是这个元素是否在这个容器里边。unordered_set,unordered_multiset,unordered_map,unordered_multimap;
vector
list
forward_list(c++11)
deque(双端) == vector(尾部)
priority_queue
set/multiset
map/multimap
(无序)unordered_set,unordered_multiset、unordered_map,unordered_multimap
容器选择原则
如果你需要高效的随机存取,而不在乎插入和删除的效率,使用vector
如果你需要大量的插入和删除,而不关心随即存取,则应使用list
如果你需要随即存取,而且关心两端数据的插入和删除,则应使用deque
如果你要存储一个数据字典,并要求方便地根据key找value,那么map是较好的选择
如果你要查找一个元素是否在某集合内存中,则使用set存储这个集合比较好
容器的函数操作请去网站查询,不做过多分析
https://zh.cppreference.com/w/%E9%A6%96%E9%A1%B5
分配器
概述
和容器紧密关联,一起使用
作用:内存分配器,扮演内存池的角色,通过大量减少对malloc()的调用,来节省内存,甚至还有一定的分配效率的提高
allocator这个c++标准库提供的缺省的内存分配器压根就没有采用内存池的工作机制。估计底层原封不动的调用了malloc();
分配器的使用
allocator分配器,是个类模板,我们写代码时极少会直接用到这个allocator这种容器的分配器;
迭代器
迭代器是指针的泛化,它允许程序员以相同的方式处理不同的数据结构(容器)
适配器
作用:转接头
容器适配器
stack:堆栈,是属于阉割版的deque;queue:队列,是属于阉割版的deque;
算法适配器(函数适配器)
bind1st,bind2nd;c++11,名字被修改为bind:
迭代器适配器
reverse_iterator
算法
算法概述
STL的算法是全局函数
头文件#include #include
算法的区间
所有算法处理的都是半开区间[begin, end)
算法词法
find_if(按照某个条件来查找)
函数对象/仿函数(functors)
常用算法
遍历算法
for_each
for_each: 用指定函数依次对指定范围内所有元素进行迭代访问。该函数不得修改序列中的元素。
void show(const int &iItem)
{
cout << iItem;
}
main()
{
int iArray[] = {0,1,2,3,4};
vector vecInt(iArray,iArray+sizeof(iArray)/sizeof(iArray[0]));
for_each(vecInt.begin(), vecInt.end(), show);

//结果打印出0 1 2 3 4
}

transform
查找算法
adjacent_find
binary_search
count
count_if
假设vector vecIntA,vecIntA包含1,3,5,7,9元素

//先定义比较函数
bool GreaterThree(int iNum)
{
if(iNum>=3)
{
return true;
}
else
{
return false;
}
}

int iCount = count_if(vecIntA.begin(), vecIntA.end(), GreaterThree);
//此时iCount == 4

find
find_if
find_if: 使用输入的函数代替等于操作符执行find。返回被找到的元素的迭代器。
假设vector vecIntA,vecIntA包含1,3,5,3,9元素
vector::it = find_if(vecInt.begin(),vecInt.end(),GreaterThree);
此时 *it==3, *(it+1)==5, *(it+2)==3, *(it+3)==9

排序算法
merge
sort
sort: 以默认升序的方式重新排列指定范围内的元素。若要改排序规则,可以输入比较函数。
//学生类
Class CStudent:
{
public:
CStudent(int iID, string strName)
{
m_iID=iID;
m_strName=strName;
}
public:
int m_iID;
string m_strName;
}

//学号比较函数
bool Compare(const CStudent &stuA,const CStudent &stuB)
{
return (stuA.m_iID<strB.m_iID);
}
void main()
{
vector vecStu;
vecStu.push_back(CStudent(2,“老二”));
vecStu.push_back(CStudent(1,“老大”));
vecStu.push_back(CStudent(3,“老三”));
vecStu.push_back(CStudent(4,“老四”));

 sort(vecStu.begin(),vecStu.end(),Compare);

// 此时,vecStu容器包含了按顺序的"老大对象",“老二对象”,“老三对象”,“老四对象”
}

random_shuffle
reverse
拷贝和替换算法
copy
replace
replace_if
replace_if : 将指定范围内所有操作结果为true的元素用新值替换。
用法举例:
replace_if(vecIntA.begin(),vecIntA.end(),GreaterThree,newVal)
其中 vecIntA是用vector声明的容器
GreaterThree 函数的原型是 bool GreaterThree(int iNum)

//把大于等于3的元素替换成8
vector vecIntA;
vecIntA.push_back(1);
vecIntA.push_back(3);
vecIntA.push_back(5);
vecIntA.push_back(3);
vecIntA.push_back(9);

     replace_if(vecIntA.begin(), vecIntA.end(), GreaterThree, 8);        // GreaterThree的定义在上面。

swap
算数和生成算法
accumulate
fill
集合算法
学习网站
https://en.cppreference.com/w/
http://www.cplusplus.com/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值