C++教程之表达式

背景

背景

C++中提供了很多操作符且定义了什么时候可以用于操作基本类型,其还允许我们定义用于操作class类型的操作符,接下来几篇文章将会介绍C++中用于基本类型的操作符,与此同时也会介绍一些库中操作符。一个表达式是由一个或多个操作符组成的并且返回一个结果。一个最简单的表达式就是一个变量或者字面量,更加复杂的表达式是由一个操作符和一个或多个操作数组成。

基础知识

C++中有一些影响表达式计算的基础概念,所以我们在使用前需要先了解一下这些基本概念。

基本概念

C++中有一元运算符也有二元运算符,一元运算符就像是地址操作符&或者是解引用操作符*,都是作用于一个操作数。类似==就是二元操作符,是作用在两个操作数上的。还有一个三元运算符,其可以操作三个操作数,函数调用则不限制操作数的数量。其中像*即可以是一元运算符(解引用符),也可以是二元运算符(乘法符号),在实际使用中由其所处的上下文环境决定。

理解这么多运算符需要理解运算符的优先级和结合性,这取决于操作数的运算顺序,以下就是一个例子,*操作符的操作数可以是10和20,也可以是是10和20/2,或者是15和20/2,该如何理解这个表达式呢,接下来我们详细说明。

操作数转换

作为计算表达式的一部分,操作数会经常从一个类型转换为另一个类型,例如二元运算符通常希望两个操作数是同一个类型,这些运算符可以用于不同类型的操作数只要两个操作可以转化为同一个类型。例如我们可以将一个整型转化成一个浮点型,但是我们无法将一个指针类型转化为一个浮点型。

运算符重载

C++定义了应用于基本类型和复合类型的操作符含义,我们自己可以定义操作符作用于class类型时操作符的含义,这种定义给了已有操作符的其他含义,这种我们称之为运算符重载。IO库中的<<和>>操作符就是重载远算符。

当我们使用重载运算符时,运算符的含义。操作数和最后的结果都取决于运算符是如何定义的,但是操作数的数量和优先级和结合性是不会改变的。

左值和右值

C++中的表达式不是左值(rvalue)就是右值(rvalue),这个名称是从C中继承过来的,其是为了好记忆,左值可以在赋值的左边,右值则不可以。在C++中左值和右值更好区分,一个左值表达式代表一个对象或者一个函数,然而需要注意的是一些左值表达式如const对象不能作为赋值操作符的左操作数。而且一些表达式产生对象但是是返回右值而不是返回左值。总的来说,当我将一个对象当作右值使用时返回的他的值,当作左值使用时返回的是其地址。

操作符在需要左值或者右值以及返回左值或者右值时是不同的,我们可以将一个左值当作右值使用,但是却不能将右值当作左值使用。

当使用decltype时左值和右值也不同,当我对一个表达式使用decltype,如果表达式返回的是左值,函数返回则是引用,例如如果p 是int*类型,decltype(p)返回的时int&,如果运算表达式是右值如地址操作符如decltype(&p)返回int*, 这是一个指针,指向一个指向int类型的指针。

优先级和结合性

一个表达式包含两或两个个以上的操作符的表达式是复合表达式,计算一个复合表达式需要将利用操作符将操作数分组,优先级和结合性决定如何分组,开发者也可以通过括号来强制分组。

通常表达式的值依赖于子表达式如何分组,操作数将会和优先级高的操作符结合在一起,例如乘和除的优先级相同,但是他们的优先级比加高,所以要先算乘除,后算加减。在优先级的相同的情况下由结合性决定如何分组,算术操作符是向左结合,这意味着在优先级相同的情况下从左向右分组:

  • 因为优先级所以3 + 4 * 5结果是23而不是35
  • 因为结合性所以20 - 15 - 3是2而不是8

运算顺序

优先级决定了操作数如何被分组,但是其并未确定哪一个操作数先被运算,在绝大多数情况下这个顺序是不固定的,例如在下面的例子中,我们知道f1和f2必须要在乘法执行前调用,然后将他们的结果相乘,但是我们并不知道f1和f2哪个先调用:

int i = f1() * f2();

对于没有指定运算顺序的操作符而言,一个表达式指向并改变同一个对象会发生错误,这么做的表达式会引发未定义的错误。一个简单的例子,<< 操作符并不指定操作数的运算顺序,其结果就是输出表达式未知。

int i = 0;
std::cout<<i<<" "<<i++<<std::endl;

有四个运算符是保证操作数的远算顺序,&&操作符先运算左边,只有左边为true才会运算右边,|| , ?:和,也会确定操作顺序。

成员获取操作符

点操作符.和箭头操作符->都能获取到成员,点操作符可以获取到class类型的成员,而箭头操作符可以获取到指针的成员:

string s1 = "a string", *p = &s1;
auto n = s1.size();
n = (*p).size();
n = p->size(); //等价于(*p).size()

由于解引用符有优先级低于点操作符,所以我们需要使用括号改变远算顺序

条件操作符

条件操作符能够简化if-else的逻辑结构,通常的使用形式如下

cond?expr1 : expr2;

其中cond是一个条件,如果cond为true,运算expr1,反之则运算expr2,例子如下

string finalgrade = (grade < 60)? "failed" : "pass";

条件操作符也可以嵌套:

finalgrade = (grade > 90) ? "high pass": (grade < 60) ? "fail" : "pass";

最后

这篇文章主要介绍了表达式,更多文章可以关注公众号QStack。

猜你喜欢

转载自blog.csdn.net/QStack/article/details/129601560