Alan Lee

给 Python 程序员的 C++ 简易入门

2020/11/22 Share

译者注:本文原文成文较早,里面一些语法可能已经不是最新的了,但是其大部分观点在我看来仍然具有点拨意义,帮助你快速对 C++ 有个认识。

本文改编自 Michigan State University 的 Rich Enbody 和 Bill Punch 的一篇文档。

本文目的

本文是给 Python 程序员的 C++ 简易入门,帮助你开始学习 C++。这不是一份完整的 C++ 入门。简洁优先于细节。

为什么是 C++?

为什么 Python 程序员要在乎 C++ 呢?

C++ 及其父语言 C 更接近于操作系统底层和硬件,所以 C++ 通常拥有更好的性能,但是也不一定。而且 C++ 也是一门通用和强大的语言,所以值得一学。如果你深入底层地去看的话,你会发现很多应用和系统使用的都是 C++ 或者 C。

当你学习 C++ 的时候要记住的一件事是一定要留意 STL:the C++ Standard Template Library,C++ 标准模板库。教科书中经常会忽略 STL,但是这是一个非常强大的工具集,而且以后会成为你的好朋友。

当然也有其他值得一学地语言,但是本文只关注从 Python 转到 C++。要注意的是 C++ 比 Java 和 C# 早,所以你可能会发现很多相似之处(和重大不同)。一旦你学会了 C++,再转到其他两门语言也相对容易。

最后,选择语言这件事也不是一个非此即彼的情况(either/or situation)。你可以用 Python 编程,然后在需要速度或者调用系统底层组件的时候使用 C++。封装 C++ 库然后给 Python 调用是一件很容易的事情(许多有用的库已经帮你封装好了)。Python 和其他语言融合很容易,有许多工具比如 Bgen、PySTL(译者注:貌似已经不怎么维护了) 和 Sip。

在 Michigan State University,我们 Computer Science and Computer Engineering 的第一个课程主要用的是 Python,第二个课程用的是 C++,Linux 环境。

Hello World!

传统的 C 编程以打印 “Hello World!” 为初学者的第一个程序。这也是第一本 C 编程书籍 —— 由 Kernighan 和 Ritchie 编写的《 The C Programming Language》的开始。由于 C++ 来自 C,我们也以这种方式开始。

1
2
3
4
5
6
7
#include <iostream>
using namespace std;
int main()
{
cout << "Hello World!";
return 0;
}

Python 程序员注意到的第一件事可能就是打印一个字符串这么简单的事情,竟然需要这么多的代码量,significant overhead。#include 就像 Python 中的 import(不完全正确,但是这个类比暂时是可以的),将 input/output stream 库导进来,也就是 cout 定义的地方。using namespace 指定了 namespace,所以我们可以直接用 cout 而不用 std::cout。(译者注:感觉类似于 `from a import `*)

int main() 表示一个函数定义的开始,函数名叫 main,花括号 {} 表示一个代码块的开始和结束,在这里就是 main 函数块的开始和结束。

cout << "Hello World!"; 将字符串传给 cout,标准输出流,即在屏幕上显示该字符串。缩进在 C++ 中是可选的,但如果你不使用缩进,那么代码可读性就会很差。如果你像在 Python 中那样缩进,那么你的代码可读性就会好很多。

Gotcha #1:C++ 语句以英文分号 ; 结尾(尽管不是严格正确,但目前来说是的)。

运行程序

C++ 是一个编译型语言,所以你在运行之前必须要编译(compile)。编译就是将 C++ 代码转成处理器能理解的机器语言。编译和运行 C++ 代码的方法根据平台不同而不同,此处我以 Linux 为例。

在 Linux 上你可以使用 VI、EMACS 等编辑器写代码。C++ 代码文件的后缀是 .cpp,所以你可以将你的代码保存为 hello.cpp

GNU 编译器 g++ 是很常见的一种编译器,你可以简单一行命令编译你的 C++ 代码:

1
g++ hello.cpp

如果编译成功,就会默认生成一个名为 a.out 可执行程序,所以你可以在终端输入 a.out 回车执行。如果编译时发现了语法错误,屏幕上会输出错误信息。

在 Windows 平台,你可以使用 Visual C++ 开发环境 Visual Studio 来写 C++ 代码,这是一个很好的软件,但是也比较复杂。编译、编辑代码都在这个软件内进行。在该软件内,编译称为 build,运行也只是其中的一个命令。

C++ 是强类型的

C++ 是一个强类型语言,虽然不是那么严谨,但是目前这样说是可以的。也就是说,所有对象都必须在使用之前进行声明。Python 可以根据值来得出对象的类型,而 C++ 必须先知道。Python 中可以允许赋不同类型的值给同一个对象,而 C++ 中必须保持一致,声明的时候就已经决定了这个对象的类型。

例如,一个变量的类型必须在使用之前进行声明。

1
int x; x = 2;

Gotcha #2:使用变量之前忘记声明。(不用担心,如果你真的忘了,编译器会提醒你的。)注意每一个语句都以分号结尾(Gotcha #1)。

C++ 类型

一些 C++ 类型你可能比较熟悉:

  • int
  • float
  • bool:和 Boolean 一样
  • string:可能需要 #include<string>

其他一些你可能不太熟悉:

  • char:单个字符,而不是一个长度为 1 的 string。

一些区别:

  • int 没有扩展精度(extended precision)。一个 int 是由机器的字长(word size)限制的,通常是 32 位,但是以后 64 位的会越来越多(译者注:限制已经是 64 位的天下了),最大值约为 20 亿。
  • int 有很多变体:shortlongunsignedunsigned long 等,但是目前你可以仅使用 int

赋值

赋值的语法和 Python 类似:

1
x = 4;

但是多重赋值(multi-assignment)是不允许的:

1
x, y = y, x

变量是不同的

C++ 中,变量是一个有名字的内存地址。声明一个变量就是给一个内存地址起一个名字,并将变量类型与这个内存地址关联起来。

1
int x, y; x = 7; y = x;

Python 中表达式 y = x 表示的是 xy 指向同一个对象(即同一个对象的两个名字),而在 C++ 中,表示的是将 x 的值复制到那个名字为 y 的内存地址上。

这个区别可以用下面这个例子来解释。在这个例子中我们可以看到改变两个指向相同对象的不同名称的变量中的一个,是否会影响到另一个变量的值。

Python 中我们可以将一个 list 命名为 A,同时也将之命名为 Bis 操作符表明 AB 指向同一个对象。如果你更改 B 的第一个元素,那么 A 也会跟着更改:

1
2
3
4
5
6
7
>>> A = [2,3] 
>>> B = A
>>> B is A
True
>>> B[0]=100
>>> A
[100, 3]

在 C++ 中实现要相对复杂一点。我们将会使用和 Python list 比较相似的 C++ array,除了 array 的大小是固定的。C++ 中你不能将一个 array 直接赋值给另一个 array。你需要使用循环来依次赋值,输出也是。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
using namespace std;

int main()
{
// declare array A of length 2 and assign its elements
// to have values 2 and 3
int A[2] = {2,3};
// declare array B of length 2, but don't assign its
// elements (yet)
int B[2];
// loop through both arrays A and B assigning to each
// element of B the corresponding value of A
for (int i = 0; i < 2; i++)
{
B[i] = A[i];
}
// change the first element of B
B[0]= 100;
// loop through A and B printing out each element
for (int i = 0; i < 2; i++)
{
cout << "A,B: " << A[i] << ", " << B[i] << endl;
}
return 0;
}

你会看到 A 的值不会随着 B 改变。

算术运算符

C++ 中的算术表达式和 Python 差不多:

  • +, -, *, /, %
  • +=, -=, *=, /=
  • 但是没有 **

自增和 Python 中不同:

  • y = ++x; 相当于 x = x + 1; y = x;
  • y = x++; 相当于 y = x; x = x + 1;

自减类似。

关系运算符

关系运算符也是差不多的:

  • ==, !=, <, >, <=, >=

但是

  • && 意思是 与
  • || 意思是 或

Gotcha #3:下面这个表达式在 C++ 中的值和在 Python 或者数学中是不一样的:假设 x = 3,那么这个表达式的值是什么:5 < x < 10

C++ 中,会先执行 5 < x,值为 false,也就是 0,然后 0 替代 5 < x 继续执行:0 < 10,结果为 true,所以整个表达式的结果是 TRUE!(这显然是和数学与 Python 中的结果是相反的。

为了得到正确的结果,你必须写成这样:

1
(5 < x) && (x < 10)

条件:if

C++ 中的 if 和 Python 中也是相似的,除了

  • 表达式要用括号括住
  • 不用冒号
  • 缩进同样不是必须的,但是强烈推荐

例如:

1
2
if (x > 4)
y = 7;

C++ 中也有 else:

1
2
3
4
if (x <= 5)
y = 8;
else
z = 10;

然而 C++ 中没有 elif,你得使用 else if:

1
2
3
4
5
6
if (y == 7)
x = 2;
else if (x != 2)
y = 5;
else
z = 10;

Python suites 就是 C++ blocks

Python 使用缩进来表示 suites,C++ 使用花括号来分隔 blocks。但缩进是强烈推荐的。

例如:

1
2
3
4
5
if ((x <= 5) && (y != 7))
{
z = 12;
w = 13;
}

注释

注释的用法也是相似的,只是不是以 # 开头:

1
2
3
// 这是单行注释
/* 这是
多行注释 */

C++ 没有 docstring。

while 循环

C++ 中 while 循环也是相似的,和 if 语句规则一样:

  • 需要用括号括住表达式
  • 花括号来标记 block

例如:

1
2
3
4
5
while (x < 5) 
{
y = z + x;
x = x + 1;
}

类似地,C++ 也有:

  • continue
  • break

但是没有与 Python 中 while 对应的 else

for 循环

C++ 中的 for 循环与 Python 比较不同,有好的也有不好的。不好的地方是 C++ 没有 Python 中方便的 for ... in ... 循环。

但是,好消息是 C++ 的 for 循环与 Python 中的 for x in range(...) 类似。

例如,在 Python 中:

1
for x in range(5): print x,

等价于 C++ 的:

1
2
for (int x = 0; x < 5; x++)
cout << x;

C++ 的 for 由三部分:

1
for (int x = 0; x < 5; x++)
  • 初始化表达式 int x =0 只在开始时执行一次
  • 条件表达式 x < 5 在每次遍历开始之前执行,如果条件为 true,那么循环体就会执行
  • 最后的表达式 x++ 在每次遍历的最后执行

注意,如果条件表达式为 false,那么循环体一次也不会执行。

while 一样,C++ 中的 for 也有:

  • continue
  • break

但是也没有与 for 对应的 else

C++中 “for “和 “while “的等价关系

C++ 中,for 是 while 的一种特殊形式。理解这一点对理解循环是如何工作的很重要。注意,这种等价关系在 Python 中不成立。

我们用一个例子来解释:

1
2
for (int x = 0; x < 5; x++)
cout << x;

等价于

1
2
3
4
5
6
int x = 0;
while (x < 5)
{
cout << x;
x++;
}

输出

C++ 的输出和 Python 相比看似相似,但是也有不同点。具体来说,Python 使用的是类似于 C 的输出表达式,这种表达式同样适用于 C++。

例如,Python 中我们可以如下进行输出:

1
print "int %d and float %5.3f" % (45, 3.1416)

C 中(也适用于 C++)也是类似的:

1
2
printf("int %d and float %5.3f \n", 45, 3.1416);
Note

注意回车符 \ncarriage return),要想得到和 Python 一样的结果,那么在 C 中必须加这个字符。

然而 C++ 标准与刚才的 C 语法有着很大的不同。

首先,C++ 需要先在程序最开始的地方加上 #include <iomanip>(与 #include <iostream> 一起)。

1
2
3
4
5
cout<< setprecision(4);
// significant digits is 4
cout<< "int "<< 45 <<" and float "<< setw(5)<< 3.1416;
cout << endl;
// endl is an explicit carriage return

文件输入与输出

此处有着非常大的不同。具体来说,对于文件输入,Python 的语法要比 C++ 短很多。

例如,Python 中我们只简单地这么写就可以了:

1
2
for line in file("Data.txt"):
print line

此外,我们也可以先显式地先打开文件 —— 一个与 C++ 更相似地流程:

1
2
3
inStream = open("Data.txt", "r")
for line in inStream:
print line

C++ 中,我们必须先导入文件流库,声明对象,显式地打开文件,显式地检查我们是否到了文件末尾:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
// include library for file streams
#include <fstream>
#include <string>
using namespace std;
int main()
{
// declare the input stream
ifstream InStream;
// a string
string line;
// open file
InStream.open("Data.txt");
// read a line and check for end-of-file (eof)
while (getline(InStream, line))
{
// output the line
cout << line << endl;
}
return 0;
}

Gotcha #4:注意 while 循环是如何工作的:我们先得到输入(使用 getline ),然后再检查是否到了文件末尾,再在循环体底部得到下一个输入。使用 while 循环时一个常见的错误是,把 getline 放到循环体的开头,从而导致循环次数过多。(这种情况也适用于 Python,不过相比 C++,在 Python 中你可能更少使用 while。)

函数

函数的使用也是相似的,但是有一些非常重要的区别,你必须非常小心!

让我们先从熟悉的地方开始。

Python 中:

1
2
def fun(x):
return x + 2

C++ 中,情况也是类似的,只不过最值得注意的地方是,需要声明参数 x 的类型。

1
2
3
4
int fun(int x)
{
return x + 2;
}

C++ 和 Python 中的函数都是只返回一个对象。然而 Python 中有内置的可以包含多个值的对象,比如元组 tuples。虽然在 C++ 中也可以构建类似的对象,但更倾向于使用参数来“返回”多个值 —— 类似于在 Python 中改变可变参数的方式。

参数:值和引用

C++ 中默认的传参方式是传值 —— 类似于 Python 中传不可变对象。即,在函数中改变相应的对象,并不会改变原始对象。换言之,改变 parameter 并不会改变 argument。

译者注:关于 parameter 和 argument,中文比较普遍的翻译分别是“形参”和“实参”。两者区别见下图,一图胜千言:

parameter vs argument
parameter vs argument,来自 https://getkt.com/blog/types-of-function-arguments-in-python/

传值

让我们先来看下 C++ 中的传值示例。输出在注释中。特别地,注意在函数中改变 x 并不会改变 main 中的 x

1
2
3
4
5
6
7
8
9
10
11
12
13
14
int fun(int x)
{
x = x + 2;
// prints 5
cout << x;
}
int main()
{
int x = 3;
fun(x);
// prints 3,即仍然保持不变
cout << x;
return 0;
}

传引用

C++ 中传引用是有一个特殊标记的:&,将这个符号加在需要传引用的参数名前。在这个例子中,函数中的 xmain 函数中的 x 指向同一个对象,所以改变函数中的 x 也会改变 main 函数中的 x (因为指向的是同一个对象)。值得注意的是,如果函数的 parameter name 是 y 而不是 x ,那么上述结论仍然是不变的。

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
int fun(int& x)
{
x = x + 2;
// prints 5
cout << x;
}
int main()
{
int x = 3;
fun(x);
// prints 5,与上面不同
cout << x;
}

类同样是相似的,但也是非常不同。

译者注:这里原文就写到这。

指针

指针对你来说可能是陌生的,我们在这里将简要地提一下 —— 它们所需要的篇幅远远超过我们在这里所能投入的。在很多方面,指针是简单的,但它的简单也会让你陷入麻烦。指针只是一个持有内存地址的变量。在一个变量声明中,星号 * 表示该变量是一个指针,类型表示该指针中的地址所对应的对象的类型。& 表示对象的地址。例如:

1
int A, *C = &A;

指针 C 存储着整型 A 的地址。下面是一个简单的 C++ 程序使用指针的例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
using namespace std;
int main()
{
int A = 15, B = 38, *C = &A;
cout << endl;
cout << &A << " " << A << endl;
cout << &B << " " << B << endl;
cout << &C << " " << C << " " << *C << endl;
A = 49;
cout << endl;
cout << &A << " " << A << endl;
cout << &B << " " << B << endl;
cout << &C << " " << C << " " << *C << endl;
C = &B;
cout << endl;
cout << &A << " " << A << endl;
cout << &B << " " << B << endl;
cout << &C << " " << C << " " << *C << endl;
return 0;
}

下面是在一个 64 位机器上的输出,注意每次运行得到的地址可能是不同的。

1
2
3
4
5
6
7
8
9
0x7ffe6212b6cc 15
0x7ffe6212b6c8 38
0x7ffe6212b6c0 0x7ffe6212b6cc 15
0x7ffe6212b6cc 49
0x7ffe6212b6c8 38
0x7ffe6212b6c0 0x7ffe6212b6cc 49
0x7ffe6212b6cc 49
0x7ffe6212b6c8 38
0x7ffe6212b6c0 0x7ffe6212b6c8 38

异常

Try/except 也是相当相似的。

译者注:这里原文就写到这。

END

CATALOG
  1. 1. 本文目的
  2. 2. 为什么是 C++?
  3. 3. Hello World!
  4. 4. 运行程序
  5. 5. C++ 是强类型的
  6. 6. C++ 类型
  7. 7. 赋值
  8. 8. 变量是不同的
  9. 9. 算术运算符
  10. 10. 关系运算符
  11. 11. 条件:if
  12. 12. Python suites 就是 C++ blocks
  13. 13. 注释
  14. 14. while 循环
  15. 15. for 循环
  16. 16. C++中 “for “和 “while “的等价关系
  17. 17. 输出
  18. 18. 文件输入与输出
  19. 19. 函数
  20. 20. 参数:值和引用
    1. 20.1. 传值
    2. 20.2. 传引用
  21. 21.
  22. 22. 指针
  23. 23. 异常
  24. 24. END