C++ 随记三 (一些杂项)
1、使用只读算法时,传参要注意:
比如,accumulate
就是个只读函数:
string sum = accumulate(s.cbegin(), s.end(), string(""));
插一嘴,最后一个参数必须是string("")
,不能只写一个空字符串""
上去,因为第三个参数的类型决定accumulate
函数调用哪种类型的+
运算符,而const char*
没有+
运算符,会产生编译错误。
2、使用equal
的注意事项
equal
不能比较两个【C风格的字符串】是否相等。
因为C风格的字符串本质是char*
,是【指针】,用equal
比较时只检查指针是否相等,即地址是否相同,不会比较其中的字符是否相同。
3、(泛型)算法不改变容器的大小
泛型算法本身不会执行容器的迭代器,它可以在容器内移动元素,但永远不会直接添加或删除元素。
所以在调用一些算法(尤其是写容器的算法)时,一定要确保 【容器的大小】 大于 【写入元素】的数量。
比如调用fill_n
函数时:
vector<int> v; 空的vector容器
fill_n(v.begin(), 10, 0); 想要写入10个0
上面的语句想写入10个元素,但是v
是空的,结果是未定义的。
但C++提供了一种插入迭代器,使得以上操作成为可能。
使用back_inserter
创建插入迭代器,它接受一个容器 或容器的引用,返回指向容器末尾的迭代器。
通过此迭代器赋值时,赋值运算符会【调用push_back
】。
vector<int> v;
fill_n(back_inserter(v), 10, 0);
在每步迭代中,fill_n
向给定序列的一个元素赋值。由于我们传递的参数是back_inserter
返回的迭代器,因此每次赋值都会在v
上调用push_back
。
4、unique
的实现方式不是删除重复值,而是将他们交换到了容器默认
5、如何把 字符串去重,并按长度排序,长度相同的按字典序排
- 先去重,去重的过程中会完成“粗暴的”字典排序,也就是:长度是乱的
- 要想按长度排,需要自己写
isshorter
函数(自命名的名字),作为【谓词】传入stable_sort
函数
注意:第二步,不能调用sort
函数,因为sort
函数无法保证对于长度一样的串,再按字典排,也就是不稳定的,而stable_sort
是稳定排序,长度相同还能保持原来的位置。
6、lambda
表达式
6.1 lambda
的作用
lambda
表达式可以让我们把函数当作【数据】一样传递,使我们从繁琐的语法中解放出来,更加关注于 “算法” 本身。
lambda
表达式完全可以取代谓词。
6.2 使用lambda
表达式有如下几点限制:
- 捕获列表只用于局部非静态变量。
lambda
可以直接使用定义在当前函数之外的名字,所以可以直接使用cout
6.3 捕获列表:
[]
不捕获任何变量,这种情况下lambda表达式内部不能访问外部的变量。
[&]
以引用方式捕获所有变量
[=]
用值的方式捕获所有变量(可能被编译器优化为const &
[=, &foo]
以引用捕获变量foo, 但其余变量都靠值捕获
[&, foo]
以值捕获foo, 但其余变量都靠引用捕获
[bar]
以值方式捕获bar; 不捕获其它变量
[this]
捕获所在类的this指针 (Qt中使用很多,如此lambda可以通过this访问界面控件的数据)
如果想改变以值的方式捕获的变量的话,要用mutable
关键字,如下图所示:
int x = 1;
auto func = [x] () mutable { return ++x; }; 此时,lambda的参数列表的一对括号()就不能省略了
cout << func() << endl;
6.4 注意事项
- 当以引用,指针或迭代器方式捕获一个局部变量时,必须保证调用
lambda
时,他们都还存在,否则会出错。值捕获就不存在这种问题,因为,只是保存了一个拷贝,永远都不用担心捕获的值没了。 - 一般来说,应尽量减少捕获的数据量,避免潜在的捕获导致的问题。而且应尽量避免捕获指针或引用。
7、二维数组做函数参数
- 在调用的时候直接把数组名字作为实参传进去就行
- 函数在声明和定义的时候必须要规定好二维数组的列数,如下图所示:而且
N
必须是个【常量】
8、transform
函数
作用:对范围内的元素进行传入的操作。
std::transform(Name.begin(), Name.end(), Name.begin(), ::toupper);
前两个参数限定了进行操作的元素范围,第三个参数是存放结果的起始位置,第四个参数是操作,可以是一个lambda
表达式。
9、bind
重排参数的妙用
按单词长度【由短至长】排序
sort(words.begin(), words.end(), isShorter);
按单词长度【由长至短】排序
sort(words.begin(). words.end(), bind(isShorter, _2, _1));
10、bind
函数需注意:非占位符参数是直接拷贝的
所以,当必须传引用时(如cout
),需要用ref
来传。如下面的例子所示:
ostream& print(ostream& os, const string& s);
for_each(s.begin(), s.end(), bind(print, cout, _1); 错误,不能拷贝一个ostream
for_each(s.begin(), s.end(), bind(print, ref(cout), _1); 正确
在bind
里要想传递一个对象而又不拷贝他,只能使用标准库函数ref
。
目前bind
有什么用呢?
答:为替代lambda
提供一个办法。当lambda
不捕获局部变量时,用函数替代它很容易,但当lambda
捕获局部变量时就不行了,这时,bind
就能做到了。
bind
接受几个参数???
bind
是可变参数的。它接受的第一个参数是一个【可调用对象】,即实际工作函数A
,返回【供算法使用】的【新的可调用对象B
】。若A
接受x
个参数,则bind
的参数个数应该是x+1
,即除了A
外,其他参数应【一一对应】A
所接受的参数。这些参数中有【一部分来自于B(_n)
】,另外一些来自于【所处函数的局部变量】。
不要把调用bind
所返回的可调用对象 和 bind
搞混了啊!Kora-a–a—a!!!
find
函数返回的可调用对象就是一个函数适配器,它是通过底层存在的原函数修改参数(修改、增加等)变成的新的函数。
11、accumulate
函数
功能,计算一段区间内所有值的和。
参数:两个迭代器,表示容器的一段区间,以及一个sum
的初值,最后返回sum + 区间内所有元素的和
12、copy
的第三个参数
说几遍才能记住???普通迭代器不能改变容器大小,所以第三个参数能是v.begin()
吗????
记住了,要传一个back_inserter
进去。
13、几种内存空间 都存了什么
- 静态内存:局部
static
对象,类的static
数据成员,定义在函数外的变量(全局变量) - 栈 内 存 :定义在函数内的非
static
对象 - 堆:我们自由使用,这是每个程序都拥有的内存池。
14、initializer_list
类型
initializer_list
类型用于向函数中传入【可变数量】的实参,但这些形参的【类型都得相同】。
initializer_list
中的元素永远是常量,无法改变
使用案例:
定义一个用到 initializer_list 的函数: (该函数用于输出不同的错误信息)
void error_msg(initializer_list<string> il)
{for (auto beg = il.begin(); beg != il.end(); beg++)cout << *beg << " ";cout << endl;
}向该函数传入不同数量的形参:(expected 和 actual 都是string类型)
if (expected != actual)error_msg( { "functionX", expected, actual} );
elseerror_msg( { "functionX", "okey" } );
可以直接使用initializer_list
初始化vector
。 如下所示:
initializer_list<string> li = { "aa", "ss", "dd", "ww" };
vector<string> v(li);
15、使用友元时发生:类型未定义
#include<iostream>class X; 提一嘴,本行声明一个类而不定义它,被称为 向前声明 (forward declaration)void func(X x) { cout << x.member << endl; }class X
{friend void func(X x);
private:int member;
};int main()
{X x;func(x);
}
上面的代码就会报出:“X”类型未定义
的错误?这是为什么??我不是在前面声明了类X
吗??
不不不,虽说你声明了类X
,但是编译器到此时对X
还一无所知啊。你在func
里使用的member
是什么东西???编译器根本不知道,所以说类型未定义。
做了类的声明以后,系统只知道有这样一个类,【并不知道有什么成员】。这时,在这个类的【声明和定义】之间就只能使用这个类型的【引用】或【指针】
在声明之后,定义之前,类是一个不完全类型(incompete type),即,已知向前声明过的类是一个类型,但不知道包含哪些成员。不完全类型只能以有限制的方式使用。
不能定义该类型的对象,不完全类型【只能用于定义指向该类型的指针和引用】,或者用于【声明】使用该类型作为【形参类型】或【返回类型】的函数。
改正上述代码:
改法一:
#include<iostream>class X;void func(X x); 这里只放个函数的声明,函数声明离职出现了类名,不会用到X的成员class X
{friend void func(X x);
private:int member;
};void func(X x) { cout << x.member << endl; } 我把函数定义放在这里,就没问题了,X都定义好了int main()
{X x;func(x);
}
改法二:
#include<iostream>class X;class X
{friend void func(X x);
private:int member;
};void func(X x) { cout << x.member << endl; } 我直接把函数的声明和定义都拿下来int main()
{X x;func(x);
}
注意!!!!!!!!!!!!!!!!!下面这样是不行的:
#include<iostream>class X;class X
{friend void func(X x) { cout << x.member << endl; }我直接声明友元的时候定义喽它,怎么样?
private: 必不行!!!!!!!int member;
};int main()
{X x;func(x);
}
虽说,可以在友元声明的时候顺便把函数定义了,但是 友元声明永远不能作为一个函数或类的声明,他只告诉编译器这是友元。即使你已经在此时此地把他给定义了。 后面想要使用该友元,必须在类外进行 单独声明 。
16、良好编程习惯(结合上一条)
先把类定义好后,再写函数实现部分
17、单独编译
可以通过编译的源文件就是一个编译单元,一个程序,可以由一个编译单元组成,也可以有多个编译单元组成。如:我们可以将所有东西都放在一个.cpp文件内,然后编译器就将这个.cpp编译成.obj,这就是一个编译单元。
c++允许程序员将组件函数放在独立的文件中,可以单独编译这些文件,然后将它们链接成可执行的程序。
一个 .cpp 对应一个 .obj,然后将所有的 .obj 链接起来(通过一个叫链接器的程序),组成一个.exe ,也就是程序了。
如果一个.cpp要用到另一个.cpp定义的函数,只需在这个.cpp文件中写上要用的函数声明。(C++在同一个项目下的不同文件都位于全局作用域下,在其他文件中也可以引用)
18、动态数组并不是数组类型
19、make_plural
函数
此函数首次出自《C++Primer 第四版》,是作者自定义的一个函数,不是哪个库里面的。
函数原型:
string make_plural(size_t ctr, const string& word , const string& ending)
{return ctr == 1 ? word : word + ending;
}
就是检查第一个参数是否是1,是1返回第二个参数,不是1返回第二个和第三个参数连起来的字符串。
20、记一个折磨半天,令我很生气的小错误
void xxx(iostream& is)
{string line;while (getline(is, line)) 这行一直报错,没有与之匹配的重载{//................}
}
wdnmd,原来是因为ifstream
不是在头文件iostream
里面。焯!!气死我了,浪费半个小时!!!
文件输入输出流定义在fstream
里面
字符串输入输出流定义在sstream
里面
21、出现:【不允许使用不完整的类型】错误,一般都是头文件没有include
全
C++ 随记三 (一些杂项)相关推荐
- 学习随记三十一——递归实现二叉查找树
递归实现二叉查找树 什么是二叉查找树:左子树中的元素都比父节点元素小,右子树中的元素都比父节点中的元素大 二叉查找树的基本操作: 1.生成一颗空树 我选择使用递归来完成,如果传入的地址非空就先递归删除 ...
- Python学记(三)turtle库
Python标准库:turtle库 Mark一下上次课的两个编程习题 获得用户输入的一个整数,参考该整数值,打印输出"Hello World",要求: ...
- 【算法随记三】小半径中值模糊的急速实现(16MB图7.5ms实现) + Photoshop中蒙尘和划痕算法解读。...
在本人的博客里,分享了有关中值模糊的O(1)算法,详见:任意半径中值滤波(扩展至百分比滤波器)O(1)时间复杂度算法的原理.实现及效果 ,这里的算法的执行时间和参数是无关的.整体来说,虽然速度也很快, ...
- 良师映照诸年---记三位印象最深的老师
俗话说,三分天注定,七分靠打拼,剩下的九十分掌握在老师手里(笑).虽是句玩笑话,但老师确实在我们的求学生涯中发挥着极为重要的作用. 见到这么佛系的寒假作业,说实话刚开始是一脸懵逼的,一时间想起小学作文 ...
- 宝付旅行记三(宁夏银川)
第三天:会议的第一天,早上5点钟就爬起来,梳洗完毕匆匆吃了早饭就直奔会场了,忙碌的一天,无话,晚上跟朋友一起来到了据说吃手抓非常正宗的同丰饭店,就在区政府斜对面,据说什么领导也常常下班过来,固定的点几 ...
- 内核随记(三)--同步(2)【转】
转自:http://blog.csdn.net/tommy_wxie/article/details/7425668 版权声明:本文为博主原创文章,未经博主允许不得转载. 2.2.睡眠与唤醒 在操作系 ...
- 企业官网页面设计谨记三个要点!
企业官网页面是企业在互联网市场中整体形象的体现,企业官网的基本建设必须体现出更专业的水平才能更精准的完成企业在互联网市场中的宣传推广.因此,我们一般建议企业选择技术更专业靠谱的网站设计公司来设计官网的 ...
- 个人应用开发详记. (三)
好久没来更新了... IM即时通讯已进入最后阶段. 各个功能模块 框架基本写好. 剩下的就是细节上的优化了 由于内容上并没有什么大幅度的变动 . 就不上图了 . 元旦回家 放假3天~ 争取年前搞 ...
- 生活随记-三十三年流水线
0岁: 出生在中部18线贫困县的山沟村子里,家中4孩,排行老大 4岁:村里水塘溺水,被发现时已无意识脸色发青,以为已经死了,村里有经验的长辈抓起我长时间倒立拍背,才侥幸苏醒活了下来,听说当时全村人都在 ...
最新文章
- 连岳读书|这是不让人害怕的数学,课堂上学不到的数学!
- JavaScript-4.2函数,变量作用域---ShinePans
- html表格横向竖向滚动,利用纯css实现table固定列与表头中间横向滚动的思路和实例...
- Exchange日志清理
- Java反射运行时_java反射获得运行时属性的值
- Android系列---JSON数据解析
- JSP 的错误调试方法
- 想变好却不能坚持,我告诉你怎么办
- 为什么excel中取消隐藏行后仍然有隐藏的行
- windows10安装keras教程
- 出书最多 [2021年12月 电子学会C语言编程等级考试二级真题解析]
- Chrome 53 支持 Shadow DOM 等规范
- syntax error, expect {, actual error, pos 1, fastjson-ve
- 4g硬盘可以装Linux 么,eeepc 900a 4g ssd硬盘予装了linux操作系统是否能改装xp
- Docker常用命令和实战演练
- Android10.0 展讯平台解锁
- CSP202112——T3登机牌条码
- Sql Server 中 GAM、SGAM、PAM、IAM、DCM 和 BCM 的详解与区别
- 多域名通配符证书和通配符证书
- Java多线程与并发库高级应用--18_传智播客_张孝祥_java5阻塞队列的应用
热门文章
- 计算机信息管理专业的职业生涯规划,计算机信息管理专业职业生涯规划书
- c++包含自定义头文件
- Worm.Win32.AutoRun.enw(ASUS.exe、ACER.exe)手动查杀
- vue打印props的值_关于Vue中props的详解
- 脱壳中的附加数据问题(overlay)
- ReactNative之Image组件自适应高度,图片自适应大小
- 根据二叉树序列构造二叉树
- 谈字符串_以及单引号与双引号等问题
- 18种根据屏幕字段查找潜在数据的技巧
- 智慧与魅力并存:盘点全球最性感迷人的 14 大创业公司 CEO