主要参考 cpp4python 和 “Learn C++: Programiz” app 上的小课, 内容是简化版中的简化版.
备用
C++ 文件的扩展名常见的有 .cpp
, .cc
等.
Hello world
#include <iostream>
int main() {
std::cout << "Hello World!" << std::endl;
return 0;
}
- 第一行可以写成
#include "iostream"
, 区别见 这里 - 必须有
main
函数, 返回类型为int
, 约定 0 表示成功, 负数表示失败. 程序执行时隐式地调用该函数. ::
相当于 Python 的.
- 在第二行加上
using namespace std;
之后下面可以直接用cout
(c stands for console)- 用
include
相当于把另一个文件的代码粘贴过来, 于是有了std::cout
这个对象. 见 C++: using namespace and #include
- 用
endl
相当于\n
Namespaces
namespace double_data {
double num;
}
int main() {
int num = 5;
double_data::num = 2.33;
return 0;
}
Atomic datatypes
int int1, int2 = 5; // usually 4 bytes (32 bits)
// Note that `3/2` = 1 in cpp (`3//2` in python)
double d1 = 2.3, d2 = 2.3e6; // preferred, 8 bytes
float d3 = 2.3f; // 4 bytes
bool flag = true, flag1 = 1; // 1 byte
// logical operators: &&, ||, !
char c = 'g'; // 1 byte, single quote
#include <string>
string s = "g"; // double quote
Pointers
// * 靠着变量名写, 更容易辨别 ptrN 是指针, varN 不是
// 从 code style 而言应该避免这么写
int *ptrN, varN;
varN = 100;
ptrN = &varN; // get the address
cout << *ptrN; // dereference
ptrN = nullptr; // `NULL` for old cpp
Flow control
if (condition) {
...
}
else {
...
}
if
是函数, 没有 elif
.
int i = 0;
while (i < 10) {
...
i++
}
for (int i = 0; i < 10; i++) {
...
}
int nums[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// C++11
for (int n : nums) {
cout << n << " ";
}
break
, continue
照常.
Functions
void func(int num) {
...
}
这样写参数传值 (pass by value)
// 传引用
void swap(int &a, int &b) {
int tmp;
tmp = a;
a = b;
b = tmp;
}
int main() {
int a = 1, b = 2;
swap(a, b);
cout << a << ' ' << b;
}
传引用 (pass by reference) 调用后能改变原来的值. The actual location in memory referenced by the arguments are sent rather than the values in that location.
直接传指针也行.
// 传指针
void swap(int *a, int *b) {
int tmp;
tmp = *a;
*a = *b;
*b = tmp;
}
int main() {
int a = 1, b = 2;
swap(&a, &b);
cout << a << ' ' << b;
}
函数调用前必须有声明 (函数名, 输入输出类型, 称为 function prototype) 或者定义 (可以先声明, 再调用, 最后定义), 如
void func(int);
重载 (overload): 相同函数名, 不同输入输出类型.
Classes
class Animal {
private:
// data member
int age = 5;
// 不写默认是 private, 只有类内能访问
// 另外还有 protected, 只有类内和子类能访问
public:
// common to all objects
// declaration
static string version;
// member function
void eat() {
cout << "eating" << endl;
}
void set_age(int x) {
age = x; // 成员变量直接用
}
// can only use static variables inside the static function
static void display_version() {
cout << version;
}
virtual void foo() {
cout << "类似抽象方法, 必须要子类覆盖";
}
// pure virtual function
// 包含这种函数的才能叫抽象类
// TODO: 那非纯的虚函数有什么存在必要?
virtual void bar() = 0;
}; // 注意分号
// TODO: 为什么这里要分号?
// definition
string Animal::version;
class Dog : public Animal { // TODO: public 作用?
public:
// function overriding
void eat() {
cout << "dog ";
Animal::eat();
}
void foo() {
cout << "wow";
}
};
int main() {
Animal animal;
animal.eat();
Animal::version = "2.3.3";
Dog dog;
dog.Animal::eat(); // 调用父类的方法
Animal *animal2 = &dog;
animal2->foo(); // 调用子类的方法
return 0;
}
class Foo {
public:
int bar, a;
// constructor, 相当于 Python 的 `__init__`
Foo(int num, int num2) {
bar = num;
a = num2;
}
// 另一种写法
// Fo
-
- o(int num, int num2) : bar(num), a(num2) {}
// It is important to initalize data members
// in the same order they are declared.
// TODO
};
int main() {
Foo foo(233);
return 0;
}
友元函数感觉没啥用, 先略.
Collections
Arrays
int data[5] = {0, 1}; // 后三个未初始化
cout << data[0];
int data2[] = {0, 1};
/* 错误写法
* int data[5];
* data[5] = {0, 1};
*/
int data[2][2] = {
{0, 1},
{2, 3}
};
for (int num : data) {
...
}
数组与函数. A formal parameter for an array is neither a call-by-value nor a call-by-reference, but a new type of parameter pass called an array parameter. 在入参数组的方括号中写数字没有意义 (语法糖, 不论写几都一样), 入参不知道数组长度, 因此需要传入数组长度参数.
double average(int nums[], int length);
根据 index 取值时, cpp 不会检查 index 合法性 (提高性能), 所以可能取到不知道什么东西的位置, 导致错误.
存储方式比较特殊.
&x[0]
等价于x
(地址)x[0]
等价于*x
(值)x[1]
等价于*(x + 1)
静态创建指编译时就分配好内存. 动态创建是运行时才决定.
int n;
cin >> n;
// allocate memory
int *ptr = new int[n];
// deallocate memory
delete[] ptr;
Vectors
Standard Template Library (STL) 中的数据结构.
更常用. Vectors use a dynamically allocated array to store their elements, so they can change size, and they have other friendly features as well.
#include <vector>
using namespace std; // 下略
vector<int> nums = {0, 0, 0};
vector<int> nums2 {0, 0, 0};
vector<int> nums3(3, 0); // 3 zeroes
int num = nums1.at(1);
// 相当于 num = nums[1] 但 at 有边界检查
双向链表
#include <list>
集合
TODO: 为什么集合有两种版本?
#include <set>
// 或者 #include <unordered_set>
Strings
#include <string>
string cpp_string = "c++ string is more modern";
char c_string[] = {"c string is an array of char with `\0` at the end"};
string str2 = "233"
cout << cpp_string + str2;
cpp_string.append(str2); // 相当于 cpp string = cpp_string + str2
Hash tables
#include <map>
// 或者 #include <unordered_map>
map<int, string> int2string = {
{1, "one"},
{2, "two"}
};
int2string[1] = "uno";
int2string[3] = "three";
for (auto item : int2string) {
cout << item.first << endl;
}
STL algorithms
#include <algorithm>
vector<int> nums = {0, 1, 2};
// 原地排序
// end 指向最后一个元素后一个的位置
sort(nums.begin(), nums.end());
File handling
#include <fstream>
fstream file_handle;
// `ios::out` writing mode, 会清空文件
// `ios::in` reading, `ios::app` append
// 模式可以写多个, 用 `|` 分隔
file_handle.open("sample.txt", ios::in);
if (file_handle.fail()) {
cout << "File not found";
exit(0); // TODO: 写错了?
}
string line;
while (!file_handle.eof()) {
// reads the file and stores it in `line`
getline(file_handle, line);
cout << line << endl;
// 写入用 file_handle << "blahblah";
}
file_handle.close();
还有 ifstream
, ofstream
, 见 这里.
Exception handling
try {
...
}
// catch string literal
catch (const char *msg) {
cout << msg;
}
catch (错误类型) {
...
}
// 写成 catch (...) 可以捕获所有 exception