八、C++中的花式操作:lambda表达式、构造函数初始化列表、三元运算符
10、lambda表达式
见mutable关键字:【C++】C++中的关键字:const、mutable、auto、new....-CSDN博客。
11、构造函数初始化列表
当我们编写一个类时,一般都要有一个构造函数对类中的成员(变量,有时也叫属性)进行初始化。
其实这个初始化的构造函数有两种写法,一种是前面示例中的赋值初始化,另一种就是现在我们要重点讲的初始化列表。
也就是类成员初始化列表也是类成员初始化的一种方式。
(1)两种初始化方式:
(2)为什么要用列表初始化?
仅仅是代码风格吗?也有这方面的因素。试想,当你A处声明的变量非常多的时候,你赋值初始化就需要很多行代码,而列表初始化代码就简洁明了很多。
但更主要的原因是:
初始化e1时,类P之所以被初始了2次是因为,一次是在A处,一次是在B处。A处虽然是成员变量区,但并不意味着不会运行代码并创建对象!所以在A处就已经无参实例化了一个小p。然后代码运行到B处,在B处又创建了一个新P实例,然后把它赋值给小p,就是覆盖第一次实例的小p。
但是当我们使用列表初始化后,就避免了两次实例化P。所以以后能用列表初始化的尽量用列表初始化,不然会浪费性能。
12、三元运算符
所谓的三元运算符就是一个问号一个冒号。它实际上只是if..else语句的语法糖。
从上面的例子中可以看到三元运算符也就是仅仅让代码更简洁,至于是否更容易理解就仁者见仁智者见智了。
或者你从这个角度理解会更理解一些:if..else语句的本质就是赋值,如上例,本质就是给b赋值,但赋值是有条件的,所以写条件?成立就是10,不成立就是5。这其实就是一个条件赋值语句嘛。
说明:上图B处的声明语句string s1;其实是会构造一个空字符串对象,然后再用一个新对象覆盖这个空字符串。所以从技术上讲更慢。因为你先构造了一个临时字符串,然后又立即销毁它。而A处没有构建中间字符串的原因实际上是与返回值优化有关,是编译器的做了优化。
其实三元运算符还可以嵌套:
虽然嵌套的可读性差了一些,但是不用写很多if,代码简洁了不少。套娃无止境,还可以花式套,比如每个条件再加一些&&或者||等,但是一般还是别花式嵌套了吧,读代码的人很容易懵。
13、运算符及其重载
C++中运算符有很多,比如数学运算符加减乘除、逆向引用的箭头->也是运算符、+=、++、--、-=、&取址运算符、<<、>>、逗号、圆括号、方括号等等都是运算符。还有一些比如前面刚讲的new和delete也是运算符,还给大家展示了new和delete的底层定义函数,以及各自重载函数。不记得的同学回看 【C++】C++中的关键字:const、mutable、auto、new....-CSDN博客
这些运算符表面上看是一些符号,其实背后都是函数,都是调用了对应的函数去执行相应的功能。或者说,就是都写成多态(同名不同参)的样子,然后调用的时候根据参数进行重载。本部分我就介绍三个样例,给大家详细展示一下什么是运算符及其重载。
(1)向量运算符示例
运算符函数究竟怎么写,主要取决于你的应用场景,你想在什么场景下怎么使用。比如我想先能生成向量,然后生成的向量还可以做加法、乘法等之类的:
上述代码虽然实现了最初的简单需求,但是我们加法用Add,乘法用Multiply,是相当麻烦的,单词拼不对就很头疼。所以我打算改成+和*,这样不仅方便,代码还会更清晰可读。此时就该重载上场了:
这样就可以重载了。而且还可以像下面的风格来写:
其实,大家更习惯的写法是上面的第二种写法,就是先写符号函数,再写正常函数。写正常函数时用指针this,并使用符号函数。
(2)std::cout中使用的左移运算符
上面的例子中我们打印向量时非常麻烦,要一个个元素打印,那是因为<<运算符没有重载现在我们自己创造的这种Vec对象,下面我们再针对Vec这个类型,写一个<<重载函数:
可见符号函数的语法是operator后面直接写你的符号,再后面是小括号里面放参数。通过这两个小例子,你现在就可以写任何你想的符号了,然后用符号替代函数。
(3)是否相等重载符
还是使用上面的例子,判断两个向量是否相等,返回bool值:
在写库时,运算符及其重载就会被经常写。
待续。。。。