当前位置:   article > 正文

【Learncpp中文翻译版】【1.9、1.10、1.11】

learncpp

原文链接:1.9 — Introduction to literals and operators
原文链接:1.10 — Introduction to expressions
原文链接:1.11 — Developing your first program

声明:

  • 本文旨在方便了解学习C++语法,切勿用于任何商业用途。
  • 由于本人英语水平有限,文章中可能存在语义错误,如有疑问请参照原文,也可以在评论区指出错误。

1.9 — Introduction to literals and operators

文字

请考虑以下两个语句:

std::cout << "Hello world!";
int x{ 5 };
  • 1
  • 2

什么是“你好世界!”和“5”?它们是字面意思。文本(也称为文本常量)是直接插入到源代码中的固定值。

文本和变量都有一个值(和一个类型)。但是,文本的值是固定的,不能更改(因此称为常量),而变量的值可以通过初始化和赋值来更改。


运算符

在数学中,运算是涉及零个或多个输入值(称为操作数)的数学计算,该值产生新值(称为输出值)。要执行的特定操作由称为运算符的符号表示。

例如,作为孩子,我们都知道 2 + 3 等于 5。在这种情况下,文字 2 和 3 是操作数,符号 + 是告诉我们对操作数应用数学加法以产生新值 5 的运算符。

在C++,操作按预期工作。例如:

#include <iostream>

int main()
{
    std::cout << 1 + 2;

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在这个程序中,文字1和2是加号(+)运算符的操作数,它产生输出值3。然后将该输出值打印到控制台。在C++中,操作的输出值通常称为返回值。
您可能已经非常熟悉数学中常用的标准算术运算符,包括加法(+)、减法(-)、乘法(*)和除法(/)。在C++中,赋值(=)也是运算符,插入(<<)、提取(>>)和相等(= =)也是运算符。虽然大多数运算符都有名称符号(例如+或= =),但也有一些运算符是单词(例如new、delete和throw)。

作者备注:
由于在我们更详细地讨论运算符时会变得清楚的原因,对于作为符号的运算符,通常将运算符的符号附加到单词运算符。
例如,加号运算符将被写入运算符+,而提取运算符将被写为运算符>>。

运算符作为输入的操作数称为运算符的arity。很少有人知道这个词的意思,所以不要把它放在谈话中,希望任何人都知道你在说什么。C++中的运算符有四种不同的算术:

一元运算符作用于一个操作数。一元运算符的一个示例是-运算符。例如,给定-5,运算符-取文字操作数5并翻转其符号以产生新的输出值-5。

二元运算符作用于两个操作数(通常称为左操作数和右操作数,因为左操作数出现在运算符的左侧,而右操作数显示在运算符的右侧)。二进制运算符的一个例子是 + 运算符。例如,给定 3 + 4 ,运算符 + 取左操作数 3 和右操作数 4,并应用数学加法产生新的输出值7。插入(<<)和提取(>>)运算符是二进制运算符,左侧为std::coutstd::cin,右侧为要输出的值或要输入的变量。

三元运算符作用于三个操作数。C++ 中只有其中一种(条件运算符),我们稍后将介绍。

空运算符作用于零操作数。C++ 中也只有其中一个(抛出运算符),我们稍后也会介绍。

注意,有些运算符有多种含义,具体取决于它们的使用方式。例如,运算符( - )有两个用处,它可以以一元形式用于反转数字的符号(例如,将5转换为-5,反之亦然),也可以以二进制形式用于进行减法(例如,4 - 3)。


链接运算符

运算符可以链接在一起,这样一个运算符的输出可以用作另一个运算符输入。例如,给定以下条件:2*3+4,乘法运算符首先执行,并将左操作数2和右操作数3转换为返回值6(成为加号运算符的左操作数)。接下来,执行加号运算符,并将左操作数6和右操作数4转换为新值10。
当我们深入探讨运算符的主题时,我们将进一步讨论运算符的执行顺序。现在,只要知道算术运算符的执行顺序与标准数学中的相同就足够了:首先是圆括号,然后是指数,然后是乘除,然后是加法和减法。


返回值和副作用

C++中的大多数运算符只是使用它们的操作数来计算返回值。例如,-5产生返回值-5,2+3产生返回值5。有一些运算符不产生返回值(例如delete和throw)。稍后我们将介绍这些功能。
某些运算符有其他行为。除了产生返回值之外,具有一些可观察到的效果的运算符被称为具有副作用。例如,当计算x=5时,赋值运算符具有将值5赋值给变量x的副作用。即使在运算符完成执行之后,x的变化值也是可观察的(例如,通过打印x的值)。std::cout<<5具有将5打印到控制台的副作用。我们可以观察到,即使在std::cout<<5执行完毕后,5也已打印到控制台。
具有副作用的运算符通常被调用为副作用的行为,而不是那些运算符产生的返回值(如果有的话)。

对于我们主要为其返回值调用的运算符(例如运算符 + 或运算符 * ),其返回值通常很明显(例如操作数的和或积)。

对于我们主要为其副作用而调用的运算符(例如运算符 = 或运算符 << ),它们产生的返回值并不总是显而易见的(如果有的话)。例如,您希望x=5具有什么返回值?

运算符 = 和运算符 <<(用于将值输出到控制台时)都返回其左操作数。因此,x = 5返回 x,std::cout<<5返回std::cout。这样做是为了可以链接这些运算符。

例如,x = y = 5计算为x = ( y = 5 )。首先y = 5将 5 赋值给 y 。此操作然后返回 y ,然后可以将 y 赋值给 x 。

std::cout<<“Hello”<<“world”的评估结果为(std::cout<<”Hello“)<<”world!“。这首先向控制台打印“Hello”。此操作返回std::cout,然后也可以使用它将“world!”打印到控制台。

1.10 — Introduction to expressions

表达式

考虑以下一系列声明:

// five() is a function that returns the value 5
int five()
{
    return 5;
}

int main()
{
    int a{ 2 };             // initialize variable a with literal value 2
    int b{ 2 + 3 };         // initialize variable b with computed value 5
    int c{ (2 * 3) + 4 };   // initialize variable c with computed value 10
    int d{ b };             // initialize variable d with variable value 5
    int e{ five() };        // initialize variable e with function return value 5

    return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

这些语句中的每一个都定义了一个新变量,并用一个值初始化它。注意,上面显示的初始化器使用了各种不同的构造:文本、变量、运算符和函数调用。不知怎么的,C++正在将所有这些不同的东西转换成一个单独的值,然后可以用作变量的初始化值。

所有这些都有什么共同点?他们使用一个表达式。

表达式是计算单个值的文本、变量、运算符和函数调用的组合。执行表达式的过程称为求值,生成的单个值称为表达式的结果。

相关内容

虽然大多数表达式都用于计算值,但表达式也可以标识对象(可以对其求值以获取对象所持有的值)或函数(可以调用该函数以获取函数返回的值)。我们在第9.2课——价值类别(左值和右值)中对此进行了更多讨论。现在,我们假设所有表达式都计算值。

计算表达式时,将计算表达式中的每个项,直到保留一个值。下面是一些不同类型的表达式的示例,并附有说明它们如何求值的注释:

2               // 2 is a literal that evaluates to value 2
"Hello world!"  // "Hello world!" is a literal that evaluates to text "Hello world!"
x               // x is a variable that evaluates to the value of x
2 + 3           // operator+ uses operands 2 and 3 to evaluates to value 5
five()          // evaluates to the return value of function five()
  • 1
  • 2
  • 3
  • 4
  • 5

正如您所看到的,文字根据其自身的值求值。变量的计算结果为变量的值。运算符(如运算符+)使用其操作数计算为其他值。我们还没有涉及函数调用,但在表达式的上下文中,函数调用的计算结果是函数返回的值。

涉及具有副作用的运算符的表达式有点棘手:

x = 5           // has side effect of assigning 5 to x, evaluates to x
x = 2 + 3       // has side effect of assigning 5 to x, evaluates to x
std::cout << x  // has side effect of printing x to console, evaluates to std::cout
  • 1
  • 2
  • 3

请注意,表达式不以分号结尾,并且不能自己编译。例如,如果要尝试编译表达式x=5,编译器会抱怨(可能是缺少分号)。相反,表达式始终作为语句的一部分进行求值。

例如,使用以下语句:

int x{ 2 + 3 }; // 2 + 3 is an expression that has no semicolon -- the semicolon is at the end of the statement containing the expression
  • 1

如果要将此语句分解为其语法,它将如下所示:

type identifier { expression };
  • 1

类型可以是任何有效的类型(我们选择了int)。标识符可以是任何有效的名称(我们选择了x<)。表达式可以是任何有效的表达式(我们选择了2+3,它使用两个文本和一个运算符)。


表达式语句

某些表达式(如x=5)对其副作用很有用(在本例中,将值5赋给变量x)。然而,我们在上面提到,表达式不能自己执行——它们必须作为语句的一部分存在。那么我们如何使用这样的表达呢?

幸运的是,很容易将任何表达式转换为等效语句。表达式语句是由表达式后跟分号组成的语句。执行表达式语句时,将对表达式求值。

因此,我们可以使用任何表达式(例如x=5),并将其转换为将编译的表达式语句(x=5;)。
在表达式语句中使用表达式时,表达式生成的任何返回值都将被丢弃(因为它未被使用)。


无用的表达式语句

我们还可以生成编译但没有效果的表达式语句。例如,表达式语句(2*3;)是一个表达式语句,其表达式的结果值为6,然后将其丢弃。虽然语法上有效,但这样的表达式语句是无用的。一些编译器(如gcc和Clang)如果能够检测到表达式语句是无用的,就会产生警告。


1.11 — Developing your first program

前面的课程介绍了许多术语和概念,我们将在创建的每个程序中使用这些术语和概念。在本课中,我们将介绍将这些知识集成到第一个简单程序中的过程。

乘以2

首先,让我们创建一个程序,要求用户输入一个整数,等待他们输入一个数字,然后告诉他们这个数字的2倍。该程序应该产生以下输出(假设我输入了4作为输入):

Enter an integer: 4
Double that number is: 8
  • 1
  • 2

我们如何解决这个问题?在步骤中。

最佳实践

新程序员经常试图一次编写一个完整的程序,然后在产生大量错误时不知所措。一个更好的策略是一次添加一个片段,确保它能够编译并测试它。然后当你确定它有效时,继续下一个片段。

我们将在这里利用这一战略。在我们完成每个步骤时,将每个程序键入(不要复制/粘贴)到编译器中,编译并运行它。

首先,创建一个新的控制台项目。

现在让我们从一些基本的脚手架开始。我们知道我们将需要一个main()函数(因为所有C++程序都必须有一个),所以如果您的IDE在创建新项目时没有创建一个空函数,那么让我们创建一个:

int main()
{
	return 0;
}
  • 1
  • 2
  • 3
  • 4

我们知道我们需要将文本输出到控制台,并从用户的键盘获取文本,因此我们需要包含iostream以访问std::coutstd::cin

#include <iostream>

int main()
{
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

现在让我们告诉用户,我们需要他们输入一个整数:

#include <iostream>

int main()
{
	std::cout << "Enter an integer: ";

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

此时,程序应生成以下结果:

Enter an integer:
  • 1

然后终止。

接下来,我们将获取用户的输入。我们将使用std::cin和operator>>来获取用户的输入。但我们还需要定义一个变量来存储该输入以供以后使用。

#include <iostream>

int main() // note: this program has an error somewhere
{
	std::cout << "Enter an integer: ";

	int num{ }; // define variable num as an integer variable
	std::cin << num; // get integer value from user's keyboard

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

是时候编译我们的更改了…
噢!以下是作者在Visual Studio 2017上获得的内容:

1>------ Build started: Project: Double, Configuration: Release Win32 ------
1>Double.cpp
1>c:\vcprojects\double\double.cpp(8): error C2678: binary '<<': no operator found which takes a left-hand operand of type 'std::istream' (or there is no acceptable conversion)
1>c:\vcprojects\double\double.cpp: note: could be 'built-in C++ operator<<(bool, int)'
1>c:\vcprojects\double\double.cpp: note: while trying to match the argument list '(std::istream, int)'
1>Done building project "Double.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

我们遇到了编译错误!

首先,由于该程序是在我们进行最新更新之前编译的,并且现在没有编译,所以错误必须出现在我们刚刚添加的代码中(第7行和第8行)。这大大减少了查找错误所需扫描的代码量。第7行非常简单(只是一个变量定义),因此错误可能不存在。因此,第8行可能是罪魁祸首。

其次,此错误消息不太容易阅读。但是,让我们分离一些关键元素:编译器告诉我们它在第8行遇到了错误。这意味着实际误差可能在第8行,或者可能在前一行,这加强了我们之前的评估。

接下来,编译器告诉您,它找不到具有std::istream类型(std::cin类型)的左手操作数的’<<'运算符。换句话说,运算符<<不知道如何处理std::cin,因此错误必须是使用std::xin或运算符<<。

现在看到错误了吗?如果你没有,花点时间看看你能不能找到它。

以下是包含更正代码的程序:

#include <iostream>

int main()
{
	std::cout << "Enter an integer: ";

	int num{ };
	std::cin >> num; // std::cin uses operator >>, not operator <<!

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

现在程序将编译,我们可以测试它。程序将等待您输入一个数字,所以让我们输入4。输出应如下所示:

Enter an integer: 4
  • 1

快到了!最后一步是将数字加倍。

一旦完成最后一步,我们的程序将成功编译并运行,产生所需的输出。

我们(至少)有三种方法可以做到这一点。让我们从最坏到最好。


不好的解决方案

#include <iostream>

// worst version
int main()
{
	std::cout << "Enter an integer: ";

	int num{ };
	std::cin >> num;

	num = num * 2; // double num's value, then assign that value back to num

	std::cout << "Double that number is: " << num << '\n';

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在这个解决方案中,我们使用一个表达式将num乘以2,然后将该值赋回num。从那时起,num将包含我们的加倍数。

为什么这是一个糟糕的解决方案:

  • 在赋值语句之前,num包含用户的输入。赋值后,它包含一个不同的值。这太令人困惑了。
  • 我们通过为输入变量分配一个新值来重写用户的输入,因此如果我们想扩展程序以稍后使用该输入值执行其他操作(例如,将用户的输入增加三倍),那么它已经丢失了。

最好的解决方案

#include <iostream>

// less-bad version
int main()
{
	std::cout << "Enter an integer: ";

	int num{ };
	std::cin >> num;

	int doublenum{ num * 2 }; // define a new variable and initialize it with num * 2
	std::cout << "Double that number is: " << doublenum << '\n'; // then print the value of that variable here

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

这个解决方案非常容易阅读和理解,并解决了在最坏的解决方案中遇到的两个问题。

这里的主要缺点是我们定义了一个新变量(这增加了复杂性)来存储我们只使用一次的值。我们可以做得更好。


首选解决方案

#include <iostream>

// preferred version
int main()
{
	std::cout << "Enter an integer: ";

	int num{ };
	std::cin >> num;

	std::cout << "Double that number is: " <<  num * 2 << '\n'; // use an expression to multiply num * 2 at the point where we are going to print it

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

这是该组的首选解决方案。当std::cout执行时,将对表达式num*2求值,结果将是num值的两倍。该值将被打印。num本身的值不会被更改,因此我们可以稍后根据需要再次使用它。

此版本是我们的参考解决方案。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/474859
推荐阅读