分类 编程 下的文章

《C++ Primer》5th 读书笔记

虽然说是这本书的笔记,但是还是会引入一些书外面的概念和新版标准的部分内容
对于不属于这本书的特殊的地方我会额外加以标注
[TOC]

第 1 章 开始

C++ 超级基础Base

可以看我之前写的关于c++的必知必会的文章

UB(Undefined-Behaviors) 未定义行为

char 是 signed char 还是 unsigned char 是未定义的
int 是大于等于 short 小于等于 long 的

流可以作为 condition,用于判断是否到了流末尾,当到达流末尾时为 false,所以我们经常可以看到如下的写法


int n;
while (std::cin >> n) {
    // do somthing...
}

此时调用了 std::cin 类的 >> 函数,往 n 中输入数据,返回的是 std::cin 类的 istream 对象,到达流末尾时(通常这个标记为 EOF(End-Of-File)),在windows上可通过 Ctrl-z 和 Ctrl-d 输入 EOF 符号,标志流结束。
>> 作为一个函数,同理

std::cout << "Hello World" << std::endl;

<< 也是一个函数,但是你会发现,其中存在多次调用,因为 std::cout<< 的返回值是一个 ostream 对象,即 std::cout 本身,所以实现了上面这样子的连续调用,但实际上,这个操作可以像下面这样分解,

std::cout << "Hello World";
std::cout << std::endl;

这两种写法是完全等价的,于是就引出了一个问题,线程安全,因为,这个操作可以被等价分成两份,所以它的操作是不原子的,就可能在别的线程中,被插入,造成输出顺序混乱,比如输出了

Hello World123123\n

但是你想要输出的是

Hello World\n123123

其中,上面的\n的是转义字符,让换行看起来更加明显。解决办法也是有的,使用原子锁,但是这个内容是在后面学习多线程时才会讲到的,所以这里不加以赘述。

字面量

字面量大致可以分为两种,一种是语言提供的字面量,一种是库提供的字面量
语言提供的字面量,例如(其实我也没有仔细研究过具体哪些属于哪些,反正把自己知道的都列出来了)

// 指针字面量
nullptr;
// 布尔字面量
true;false;
// 整数字面量
auto a = 1;        // int a
auto b = 0x1;      // int b
auto c = 01;       // int c
// 二进制字面量在 c++14 中引入
auto d = 0b1;      // int d
auto e = 1u;       // unsigned int e
auto f = 1ll;      // long long f
auto uf= 1ull;     // unsigned long long f
auto g = 1l;       // long g
// 其实这些后缀大小写都是可以的,为了方便书写,这里都写小写
// 浮点数字面量
auto h = 1.0l;     // long double h
auto i = 1e-1;     // double i
auto j = 1.0f;     // float j
auto k = 1e-1f;    // float k
// 字符(串)字面量
auto l = "Hello";  // const char *l
auto m = u"World"; // const char16_t *m
auto n = U"你好";  // const char32_t *n
auto o = u8"世界"; // const char8_t *o
auto p = L'!';    // wchar_t p
auto q = '!';      // char q

但实际上,部分字面量会根据自己的数据大小自动变更数据类型,如果数据超过了long所能承载的范围,就会自动变为 long long,类型都是所能承受的最小类型,当然,char和short不在此列;
用户自定义字面量
使用运算符重载的方式

auto operator"" end(something) {

}

就可以弄一个 somethingend 的一个字面量,something 作为参数Parameter传入 函数,处理后返回

第 I 部分 C++ 基础

第 2 章 变量和基本类型

初始化与赋值

其实这个是老生常谈的问题了,因为总是有非常多的事情,在这个上面纠结

int a = 10;  // mov     DWORD PTR [rbp-4], 10
int b;       // 
int d = a;   // mov     eax, DWORD PTR [rbp-4]
             // mov     DWORD PTR [rbp-8], eax
int c = b;   // mov     eax, DWORD PTR [rbp-12]
             // mov     DWORD PTR [rbp-16], eax
b = 10;      // mov     DWORD PTR [rbp-12], 10

左侧为 c++ 代码,右侧为生成的汇编。这就很神奇了,你发现第二行定义变量 b 的时候,没有生成任何代码。在你尝试书写

int a = 10; // mov     DWORD PTR [rbp-4], 10
int b;      // 
b = 10;     // mov     DWORD PTR [rbp-12], 10

如上的代码的时候,生成的汇编也只有两行,就算你开 -O0 也一样,因为这个定义的语句,的确,什么事情也没有干,只是告诉编译Compiler器,「这个位置我占掉了,虽然里面的东西我没有明确给它,但是我占了,你得让接下来的变量都往后挪挪,而且不能说我不在」的这种状态。

类型、限定符、修饰符

int, char, short, long, long long, float, double, long double 等被称为基础数据类型,一切的一切基于此而产生

分析一个变量的具体类型,应该从右王座看,看到 & 就是引用ref类型,看到 [] 就是数组类型,看到 *就是指针类型,然后看 顶层const还是底层const

人们喜欢讨论 * 这个字符用于限定变量的时候的位置,就会产生下面三种结果

int *p1;  // *p1 的类型为 int
int * p1; // 两边各退一步
int* p1;  // p1 的类型为 int*

我个人倾向于使用 int* 的方式,将其作为一整个类型,因为 c++ 的类型系统过于复杂,比如

int x = 20;
int* px = &x;
decltype(x) px2 = px;

如果说是 *p 的类型为 type 的话,应当如何解释 decltype(x) 所推断出来的类型?所以,我倾向于使用 type* nameauto px3 = px;也是如此,如果不是类型,何来「类型推断」?
同理,引用类型也是这样

// 指针
type* name1;
// 引用
(type*)& name2 = name1;
// 上面这个是对于指针类型的引用,引用必须要初始化

下面讲讲关于 const 和 constexpr

在此之前,真正的 constant 实际上是使用 #define 来定义的,但是 c++11 出现了 constexpr,终于可以用正经的方式定义一个真正的 constant 了。
constexpr 要求变量或者表达式的值能够在编译器得到计算,于是乎,用 constexpr 修饰的变量,是一个定值
const 所代表的是,不变量,与变量相对,只是在使用的过程中不会发生变化,但不代表它是一个固定的值

int a;
std::cin >> a;
const int b = a;

合法么,合法,但是 b 的值会随着我们的输入而发生变化,但是在接下来试图改变b的值,都会造成编译器错误。
但其实也不一定的,虽然说不能直接通过b修改b所对应的对象值,但是我们可以通过间接的方式,访问到b,并修改,而且不会引发编译器错误。
这个我们可以拿 nim 中类似的语句进行对比

const str = "Hello World"
# Mutable variables
var c: int
c = 20
# Immutable variables
let e = c

nim 中
const 是常量,var 是变量,let 是不可变量
c++ 中
constexpr 是常量,没有限定符的各种类型 是变量,const 是不可变量
这样一比其实也不难发现 c++ 在发展上的滞后性了

const int i = 20;
// 不能修改的 int 类型变量(不变量也是不会变的变量)
const int* const pi = &i;
// 首先,a 是 const,a 本身的值不能改动
// 其次,是 const int* 类型,代表是 cosnt int 类型的指针
// 说明 a 所指向的对象是 int 且不能修改
const int*& const rpi = pi;
// 一个 const int* 类型的引用,且本身也不能修改,(虽然说引用本来就不能改,不知道加上有没有

我们再加上一个数组类型,数组类型就非常有趣了,因为其中的矛盾点实在太多了,比如可以弱化成指针,这一点就很折磨人,所以和别人解释,但是这个内容再在下一章解释

自动类型推断

auto
decltype(statment)
decltype(auto)     //c++17 引进

自定义数据结构,使用 struct 将各种数据归为一类,
但是好像没有看到 union 这个数据结构在这本书中被介绍
虽然说用的少,但其实,还是很有用的,比如用作动态类型的数据结构

第 3 章 字符串、向量和数组

其实这一章前面部分没有什么特别重要的事情,只不过介绍了 std::string 和 std::vector 两个标准库「容器Container
我想这个内容可能看 C++ 标准库 可能更加适合一些
但有一点可以注意的就是

std::vector<std::string> a(10, "20");
std::vector<std::string> b{10, "20"};

的效果是一样的

迭代器

std::string::itrator;
std::vector<int>::itrator;

数组

int a[10];
using int10 = int[10];
int10 b;

第 4 章 表达式

提升,转换,重载,左右值

赋值,取址,解引用和下标……
都必须要使用左值,

decltype 对左值,取到的是一个引用,但对于右值,取到的是本来的类型
这个在之前的例子中有出现过

int i = 10;
int* pi = &i;
decltype (*pi) d = i;  // 实际上这是 int& 类型
decltype (pi) e = pi;  // 这个是 int* 类型
sizeof int
sizeof (1+1)
int a = f1() + f2();

像上面这两个函数返回值相加,但是你没有办法知道先执行的是哪一个函数,这个是不确定的,一定程度上也是线程不安全的

T operator+(const T &a, const T2 &b);

同理,因为加号会图上面这样子重载,对于某一个类型的重载,你也可以把int类型的+重载成*,这个以后再讲。f1和f2就成为了 operator+ 的两个参数,这样同样表明,函数作为函数参数时,其运行顺序也是不确定的

取余的运算比较复杂,日后重新罗列

第 5 章 语句

if

if-else

for

for

while

do-while

switch

break

continue

goto

throw

try

catch

第 6 章 函数

const 的故事到这里,才算真正地开始……

整个 c++11 就是一个类型斗争史,auto,decltype,template,using,透露出两个字,类型,类型,还是类型,函数重载、尾置返回、左值引用、右值引用也涉及到类型推断,
尾置返回类型

auto func (int i) -> int(*)[10];

函数参数中的顶层 const 会被忽略掉

void func (const int i);
void func (int i);

上面两个声明其实都是一个函数,会报错

void print (const int* i);
void print (const int i[]);
void print (const int i[10]);

上面三种声明,也是一样的,所以,在作为函数参数的时候,数组会弱化为指针

const int& i = 41;
void func (int& ar[]);   // 引用的数组
void func (int (&ar)[]); // 数组的引用

函数返回数组指针

int (*function(void))[10];  // 一个返回指向 int[10] 的指针的函数
// 使用尾置返回
auto function(void) -> int(*)[10];
using pi10 = int(*)[10];
pi10 function (void);

函数返回函数指针

int (*function (void)) (int*, int);
auto function(void) -> int(*)(int*,int);
using ifpii = int(*)(int*, int);
ifpii function (void);

关于如何向数组传入长度参数,其实还有别的小妙招

template <typename T>
void func (int& ar[T]) {

}
type (*function(parameters))[dimension] {

}

可以定义一个返回 数组的指针 的函数

折叠表达式(C++17 起)

template<typename... Args>
bool all(Args... args) { return (... && args); }
template<typename... Args>
bool any(Args... args) { return (... || args); }
template<typename... Args>
bool sum(Args... args) { return (... +  args); }

bool b = all(true, true, true, false);
 // 在 all() 中,一元左折叠展开成
 //  return ((true && true) && true) && false;
 // b 为 false
// 将一元折叠用于零长包展开时,仅允许下列运算符:

// 1) 逻辑与(&&)。空包的值为 true
// 2) 逻辑或(||)。空包的值为 false
// 3) 逗号运算符(,)。空包的值为 void()
// 注解
// 若用作 初值 或 形参包 的表达式在顶层具有优先级低于转型的运算符,则它可以加括号:

template<typename ...Args>
int sum(Args&&... args) {
//    return (args + ... + 1 * 2); // 错误:优先级低于转型的运算符
    return (args + ... + (1 * 2)); // OK
}

函数重载匹配其实也是一个很复杂的问题,
但是拒绝认识它也是可以的,就是不要写出具有歧义的重载函数

第 7 章 类

访问控制
public private protect
友元
friend
类成员
作用域和名字查找
构造函数初始化列表
委托构造函数
默认构造函数
=default =delete
隐式类类型转换
explicit

第 II 部分 C++ 标准库

第 8 章 IO 库

头文件 类型 描述
iostream istream, wistream 从流读入数据
ostream, wostream 向流写入数据
iostream, wiostream 读写流
fstream ifstream, wifstream 从文件流读入数据
ofstream, wofstream 向文件流写入数据
fstream, wfstream 文件读写流
sstream istringstream, wistringstream 从 string 读入数据
ostringstream, wostringstream 向 string 写入数据
stringstream, wstringstream 读写 string

c++ 定义了上面这些流操作的类型,提供了最基础的流抽象功能,其他功能也可以基于此进行更深的抽象

但是,C++ 的流可能是一个非常失败的设计,因为输入输出的符号大家都不是很喜欢,而且在前期缺少合适的文本格式化工具,直到 c++20 引入了 format 库,才有所改观,但实际上现在没有任何一家的编译器是支持 format 库的再者是自带的 STL 库都没有提供对于标准化输入输出流的支持,只能自己手动输入输出。

但是我们也可以通过这个设计实现一个统一的输入输出操作

可以通过若干种标志判断当前流的状态处于失败、结尾、正常或者是异常
也可以通过对应的函数设置当前的状态

unitbuf, nounitbuf, flush, endl
立即刷新,不立即刷新,强制刷新,强制刷新并换行

我们可以使用 fstream 完成对文件流的读写操作,其具体操作与输入输出流并没有特别多的区别,唯一的是需要指明文件的路径和打开方式

需要注意的是,文件的写操作默认是附带 std::ios::trunc 的,这个意味着打开一个文件的时候,如果原先存在文件,则会将原先的文件删除

使用 stringstream 则可以对流进行细分,在实际使用中出现频率还是很高的

第 9 章 顺序容器

顺序容器主要有 类型 介绍
vector 动态数组,即长度可变,支持快速随机Random访问,数据连续存储,所以插入数据可能很慢
deque 双向队列,长度可变,支持快速随机访问,头尾插入删除很快,数据连续存储的同时分块存储
list 双向链表,长度可变,随机访问并不快速,任意一个元素的插入删除都很快,就链表的存储方式
forward_list 单向链表,长度可变,随机访问也不快速,但是相比双向链表少了一个方向,所以在插入和删除时比自己手写的链表快不了多少
array 静态数组,长度不可变,可以看作是原生数组的高级版本
string 和vector类似,但是专门用于保存字符,同时提供大量字符串处理相关的函数
queue 单向队列,由双向队列继承而来
priority_queue
stack 栈,由双向队列继承而来

其实这里有一个很巧妙的点,为什么说是快速随机访问呢,确实,链表因为数据结构的问题,其实不支持随机访问,但是可以通过遍历的方式,实现一个非常慢速的随机访问,但也其实,可以通过一个vector存储链表的迭代器,再对vector随机访问,就可以实现对链表的随机访问了,这个适用于大规模的对于链表的随机访问

同时,这些容器库之所以要叫容器库,是因为它们提供了对任意类型的「容」,这个得益于 c++ 复杂的模板,在编译器对各种类型进行展开,同时,由于模板的存在,很多本来看上去很正常的名字,就变得极为不正常了,这也导致使用了模板的报错变得异常难读,学会从模板报错中找到正确的错误,也是一个非常重要的技巧

这些容器库,STL,都包含着若干统一的操作函数,但这里就不一一列举了,这不应该成为学习 c++ 的负担。诸如比较,构造,复制,交换,添加删除,以及各类迭代器(c++11 引入了一种新的反迭代器,还有各种容器的构造,赋值,交换,追加,插入,删除,移动,拷贝,这里也不加以细说。

往容器中添加元素又变得非常有复杂,但也没有那么复杂
push_back, push_front, emplace_back, emplace_front, insert,其中 emplace 系列函数于 c++11 引入,究其原因还是因为 c++11 带来的右值引用,push 系列函数在插入一个值的时候,会先对值进行拷贝,但是 emplcae 函数借用右值引用直接将值写入对应位置,减少了一次拷贝,一定程度上提升了性能(右值引用牛逼!)
https://zhuanlan.zhihu.com/p/213853588
pop_back, pop_front, erase 用于删除元素
各个迭代器的使用,forward_list独树一帜的特殊操作

对于容器的插入删除可能导致迭代器失效,因为移动了容器中实际内容的位置,vector在内容将要填满预先分配的空间时,会将当前空间扩大为两倍,使用capacity可以查看已分配的空间,size查看已使用的空间

对字符串的各种操作函数在这里也不加以赘述了,实际上字符串库应当搭配c++11引入的regex正则匹配库和c++20引入的format格式化库使用更加顺手,正则匹配是一个好东西,就是看起来效率非常差,但也没有那么差了

第 10 章 泛型算法

泛型,何为泛型,即通用的,对于任何类型都可以使用,类型无关
这些泛型函数主要通过迭代器和传入的函数进行使用
其中大多数函数定义在 algorithm 算法库中,c++11 提供了超过一百种的内置算法,为开发提供了非常有用的帮助,尤其是 sort 函数我使用的次数不可谓不多
find, find_if...
(我会在将来的某天详细地介标准库的内容,但不会是在这本书上
泛型算法主要分为只读算法,写算法和排序算法
查找算法,判断算法等算法为只读算法
写算法,插入算法(使用插入迭代器),拷贝算法(利用迭代器),
排序算法,sort不稳定排序,stable_sort稳定排序,性能上各有千秋

之后,这本书在这个地方介绍了一个非常重量的 c++11 更新,lambda 函数,同时,这也更一步的让c++拥有了函数式编程的风范,一个较为简单的方式声明并使用一个函数,其中的详细内容我会另开一个文章进行介绍,但书中没有在这里引入function函数用来存储lambda函数我感觉还是有些欠妥当,不过也介绍了bind函数关于绑定函数参数的内容,同时介绍了find_if 函数和for_each 函数。值捕获,引用捕获,隐式捕获,等等等等,设置返回值,自动推断返回值,这里甚至还产生了闭包,但是在这本书里貌似没有介绍到。在lua中,闭包是一个非常重要的概念,而且在介绍lua的书中大书特书了

这里详细介绍了插入迭代器和反迭代器

第 11 章 关联容器

1)set or map 2)可否重复 3)有无顺序
因此产生了八种不同的数据类型,分别是:set map multiset multimap unordered_map unordered_set unordered_multimap unordered_multiset

因为map存储了两个信息,这里还引入了一个新的对象 pair,用于构成一对的数据结构,分别存储 map的key和value。关联容器同样拥有普通容器的大部分操作。之所以叫做关联,是因为key之间是相互影响的。比如在map和set中,是不允许出现相同的key的,这叫关联。

关联容器可以使用任意类型当作key和value,总之非常有用,但是具体的操作并不在这里赘述。一个非常有用的地方,就是统计单词的数量和有多少种单词,map的key为单数,value为数量,即可进行统计。set的key为单词,即可统计单词的种类,因为set在数学上与集合的含义相类似,所以在这里其实对于set还有非常多的集合操作,这本书上也没有讲。
然后我也没什么好讲的了,毕竟关于标准库的介绍不应该成为负担

第 12 章 动态内存

从程序支持手动申请内存开始,人类就陷入了无限的与指针的抗争之中。人们为了正确处理这些内存,掉了数不清的头发。所有分配的对象,需要一个能够指向它们的指针才能调用。手动分配的对象不受作用域或者生命周期影响。但是用来存储这个指针的对象,存在作用域和生命周期,当语句块结束后,这个指针变量则会消失,也许指针变成返回值传到另一个变量之中,也许没有。如果没有的话,那么这个内存中的对象就彻底变得无主了,于是这个内存中的对象就无法已正常地形式进行清除了。

这就形成了垃圾,于是我们引入了垃圾回收的概念,这个在相当多的语言中都有直接的体会,但是,这么方便为什么c++不用呢?因为垃圾回收,浪费空间,也浪费性能,所以这个功能不会在c++中提供,诸如python和lua,会对一些垃圾自动「标记-清理」。如果尝试写python,循环地进行一些事项,你有可能发现自己的内存占用,忽高忽低,这个就是python垃圾回收的效果了。

智能指针

但是c++难道没有办法高效地实现垃圾回收了么?当然是有的。答案就是使用智能指针,智能指针是在C++11中引入的。既然问题出现在指针上,那么解决这个指针,那么所有的问题就迎刃而解了。借助 c++ 类所带来的 RAII 功能,我们可以轻松实现,创建时如何,销毁时如何的功能。这也为智能指针的出现,奠定了基础。

智能指针分为三类
shared_ptr
unique_ptr
weak_ptr

其中,shared_ptr可以被赋值,拷贝,即可以存在多份,每次赋值拷贝会调用对应的构造函数,返回这样一个指针也会产生拷贝。每一次执行上述的操作时,其内部的计数器,会让自己的值增加一,只要内部计数器的值达到零,就会自动销毁内存中的对象。当然,这个时候,智能指针对象也肯定不会存在的。其中,weak_ptr是不会增长这个计数器,但是,当目标对象被销毁后,使用 weak_ptr 又成了一个未定义行为。unique_ptr不允许拷贝、赋值等操作,只能单一存在

std::shared_ptr<std::string> sps(new std::string);
std::shared_ptr<std::string> sps2(sps);
std::shared_ptr<int> spi(new int[100])
std::unique_ptr<std::string> ups(new std::string)

手动分配、管理内存

使用 new 和 delete 关键字,即可完成对象的申请和销毁。但是这里有一个小小的问题,与我们之前所讲的东西有所不同的是,new 所返回的并不是如我们所想的 数组的指针,它直接返回的只是一个指针,我们在这个过程失去了数组的大小,而且你甚至不能判断它就是数组。

int* i = new int;     // 创建一个 int 对象
int* is= new int[10]; // 创建一个 int[10] 对象
delete i;             // 销毁一个 int 对象
delete [] is;         // 销毁一个 int[10] 对象

int* pi = new int[0]; // 创建一个空对象
// 实际上这句话什么事情也没有做,pi 所指向的值是未定义的

先分配内存空间,再进行初始化赋值

std::allocator

如果之前已经尝试过大量代码的同学,可能早就发现在使用STL的过程中,有一些报错的模板展开后,就有std::allocator类,

第 III 部分 类设计者的工具

第 13 章 拷贝控制

拷贝构造函数

Foo ();          // 默认构造函数
Foo (const Foo&) // 拷贝构造函数

合成拷贝构造函数,即默认的拷贝构造函数,会将源对象的所有内容拷贝到目标对象

std::string dots(10, '.');  // 直接初始化
std::string s(dots);        // 直接初始化
std::string s2 = dots;      // 拷贝初始化
std::string null_book = "9" // 拷贝初始化
std::string nines = std::string(100, '9')
// 拷贝初始化

拷贝赋值函数

Foo& operator= (const Foo&); // 拷贝赋值

同样的,拷贝复制也有合成拷贝赋值运算

移动构造函数

移动赋值运算符

析构函数

析构函数作为一种在对象生命周期结束的时候调用的一个函数,等同于给对象擦屁股的作用。于是,C++也提供了 RAII 等一系列功能。

生命周期如何结束:变量离开作用域,父级对象被销毁,容器被销毁,delete主动销毁,临时变量创建完整的表达式之后

C++ 三/五法则

当定义一个类时,我们显式地或隐式地指定了此类型的对象在拷贝、赋值和销毁时做什么。一个类通过定义三种特殊的成员函数来控制这些操作:拷贝构造函数、拷贝赋值运算符和析构函数。

拷贝构造函数定义了当用同类型的另一个对象初始化新对象时做什么,拷贝赋值运算符定义了将一个对象赋予同类型的另一个对象时做什么,析构函数定义了此类型的对象销毁时做什么。我们将这些操作称为拷贝控制操作。

  由于拷贝控制操作是由三个特殊的成员函数来完成的,所以我们称此为“C++三法则”。在较新的 C++11 标准中,为了支持移动语义,
  又增加了移动构造函数和移动赋值运算符,这样共有五个特殊的成员函数,所以又称为“C++五法则”。
  也就是说,“三法则”是针对较旧的 C++89 标准说的,“五法则”是针对较新的 C++11 标准说的。
  为了统一称呼,后来人们把它叫做“C++ 三/五法则”。

“需要析构函数的类也需要拷贝和赋值操作”
  从“需要析构函数”可知,类中必然出现了指针类型的成员(否则不需要我们写析构函数,默认的析构函数就够了),所以,我们需要自己写析构函数来释放给指针所分配的内存来防止内存泄漏。
  那么为什么说“也需要拷贝构造函数和赋值操作”呢?原因是:类中出现了指针类型的成员,必须防止浅拷贝问题。所以需要自己书写拷贝构造函数和拷贝赋值运算符,而不能使用默认的拷贝构造函数和默认的拷贝赋值运算符。

“需要拷贝操作的类也需要赋值操作,反之亦然”

“析构函数不能是删除的成员”
  如果析构函数是删除的,那么无法销毁此类型的对象。对于一个删除了析构函数的类型,编译器不允许定义该类型的变量或创建该类的临时对象。而且,如果一个类有某个成员的类型删除了析构函数,我们也不能定义该类的变量或临时对象。

让编译器使用合成

class Foo {
public:
    Foo () = default;
    Foo (const Foo&) = default;
    Foo& operator= (const Foo&);
    ~Foo () = default;
};
Foo& Foo::operator= (const Foo&) = default;

阻止拷贝

如果要阻止拷贝,就把上面对应的default换成delete删除即可,但是delete必须出现在成员第一次声明的地方,即

class Foo {
public:
    Foo () = default;
    Foo (const Foo&) = default;
    Foo& operator= (const Foo&) = delete;
    ~Foo () = default;
};

其中,析构函数若被删除,我们就无法释放这些对象

如果一个类有数据成员不能默认构造、拷贝、复制或者销毁,则类对应的成员函数将被定义为删除。其原因是为了避免所创造的对象无法被销毁

但是这个功能是在c++11中引入的,在此之前,我们可以通过把对应的成员函数定义为private,外部的环境则无法访问对应的成员函数,实现了删除。声明但不定义是合法的,但是当使用这个函数时,会报链接错误,即找不到对应的函数定义,通过private声明,则可以阻止用户(我们)调用private函数,实现控制。

试着联系之前出现过的 std::unique_ptr 的禁止拷贝

尝试书写自己的资源管理类

值一样的类和指针一样的类,对应了之前的string类型和智能指针

动态内存管理类(std::allocator 续)

教你怎么实现 vector 类

申请一块内存区域,然后使用 allocator 复制内存区域到新申请的区域

对象移动和移动语义

为了避免在前面管理内存的时候,出现无意义的拷贝赋值,所以在c++11引入了右值引用,减少了对数据的拷贝,提升了效率

int i = 42;           
int& ri = i;             // 左值引用
int&& rri = i;           // 编译错误,左值不能绑定到右值引用上
int& ri2 = 42 * i;       // 编译错误,右值不能绑定到左值引用上
const int& cri = 42 * i; // 右值可以绑定到常量左值引用上
int&& rri2 = 42 * i;     // 右值引用

右值是临时的,而左值是永久的(在作用域内是永久的)
右值引用的好处是可以延长临时变量的生命周期。其基础上也实现了移动语言std::move和完美转发std::forward
能出现在等号左边的就是左值,右值只能出现在等号右边

使用移动操作时,要标明函数是不抛出异常的,否则会为此做一些额外的工作

class StrVec {
public:
    StrVec (StrVec&&) noexcept;
};
StrVec::StrVec (StrVec&& s) noexcept : {
    // ...
}

引用限定符

class Foo {
public:
    Foo& operator= (const Foo&) &;      // 只能向可修改的左值赋值
};
Foo& Foo::operator= (const Foo& rhs) &;

第 14 章 重载运算与类型转换

不可重载的运算符

大多数运算符都是可以重载的,但是有5个运算符C++语言规定是不可以重载的.

  1. .(点运算符),通常用于去对象的成员,但是->(箭头运算符),是可以重载的
  2. ::(域运算符),即类名+域运算符,取成员,不可以重载
  3. .*(点星运算符,)不可以重载,成员指针运算符".*,即成员是指针类型
  4. ?:(条件运算符),不可以重载
  5. sizeof,不可以重载

C++ 只允许使用原本存在的运算符,而不支持自定义运算符,但是如果实现了这个功能,c++ 马上又起飞了

c++11 还引入了用户自定义字面量,似乎这个功能并没有在这本书中得以体现

long double operator"" pi(long double x) {
    return x*3.14159265357;
}
long double operator"" pi(unsigned long long x) {
    return static_cast<long double>(x)*3.14159265357;
}

通过如上的代码我们可以实现自定义字面量,实现了xpi的数学写法,当然也可以给自然底数加上这样的功能

auto rad = 3pi;
auto rad2 = 4.0pi;

c++20 其实还引入了一个新的运算符<=>三向比较运算符,俗称,飞碟运算符,这个就是后话了,这里也不赘述

重载运算符的使用

之前在一个群里听到有人吐槽 std::string 没有重载与部分类型的 + 运算,然后我就丢给了他这样子的代码

std::string operator+ (std::string str, int x) {
    return str+std::to_string(x);
}
auto str = std::string ("H") + 123;  // "H123"

实现了 std::string 与 int 类型的直接相加
当然,上面相加的一行,也等价为

auto str = operator+ (std::string("H"), 123);
// 同理
auto i = operator+ (123, 321);
// 也是成立的,但是 operator 只能同时有两个参数,而且写起来也格外麻烦

要注意的是,尽量不要使重载后的运算符的含义偏经离义,那会让使用者困惑。

又往下看了一些,发现 std::string 并没有把 + 作为自己的成员函数,而是当成了普通的非成员函数,所以也实现了 const char + std::string 的功能,如果是成员函数的话,const char 是不能放在前面的

输入输出运算符也是如此,应当作为非成员函数存在才能正常使用,不然以
ostream.>>(Foo) 的调用形式,是不能正确实现期望的功能的

ostream& operator<< (ostream& os, T t) {
    // ...
}

运算符介绍

算术运算符
无非就是加减乘除余和各种二元运算符
逻辑运算符
逻辑运算符和算术运算符使用方法基本一致
赋值运算符
普通的赋值运算符没有什么特别的,但是复合赋值运算符就有一些不同了,只传入一个参数,其中,左侧的被赋值对象的 this 指针会被传入
下标运算符
通常是返回访问元素的引用为好,此时可以多重下标运算
递增递减运算符
这里又有点小不同了,递增递减分为前置和后置,但是运算符重载总是以 operatorOPR的形式存在的,应当如何区分前置和后置呢?

int& operator++ ();    // 前置
int& operator++ (int); // 后置

后置递增运算符中,虽然出现了一个额外的参数,但是这个参数是不应当被使用到的,编译器为默认往里面传入0。此行为只是为了区分前后置

显式调用递增运算符

Foo p;
p.operator++(0); // 后置
p.operator++();  // 前置

成员访问运算符
虽然我们不可以重载点成员访问运算符,但是我们可以重载箭头成员访问运算符
函数调用运算符
重载这个运算符,可以让我们的类对象表现得像函数一样

Foo foo;
foo(123); // 此处已然不是构造函数

类型转换运算符
书中把这个内容放在了 lambda 的下面,但是我把它提了上来,放在了一起。类型转换函数一般形式如下:

operator type() const;  // 显式类型转换运算符

我们再一次联系之前所学到的内容,explicit

explicit type() const;

只有当我们尝试使用显式类型转换时,才会调用这个函数

static_cast<type> a;

书看到这里,也差不多能够解决我的一个疑问了,我当初就想,为什么一个流对象在循环中可以被当作条件使用,现在发现因为有隐式类型转换运算符重载的出现,所以在条件中,流被转换成一个bool类型的值返回,使循环可以正常运行

记得避免二义性转换

lambda 表达式再续前缘

cppreference 上面写道:lambda 就是创建一个闭包并返回,但是可能闭包这个概念解释起来也是过于复杂,所以 C++ primer 真的也只是做了一个 primer,从而不介绍具体的编程范式,这个大概可以在别的书中看到具体的操作方式。当然,网上也有很多类似的教程
c++ 是一个多范式编程语言,虽然在 lambda 出现之前就已经实现了函数式编程的功能,但是,lambda 表达式的存在,第一次让书写函数变得如此简练,写函数写起来真的是非常的爽快

于是乎,c++ 标准库为了符合的上自己的多范式编程语言的称号,也在 functional 中定义了一系列的函数式范式的类,用于生成对应的函数对象提供进一步的操作

算术 关系 逻辑
plus<T> equal_to<T> logical_and<T>|
minus<T> not_equal_to<T> logical_or<T>
multiplies<T> greater<T> logical_not<T>
divides<T> greater_euql<T>
modulus<T> less<T>
negate<T> less_equal<T>

可调用对象与function

今天下午,巨佬刚好怼着 std::function 喷了一堆,但是我也看不懂,他反驳的是 c++ 标准库中存在的那些糟粕,嫌弃 std::function 的性能之差,说 noexcept swap allocator 等东西满天飞,到 2021 年还没有解决,自己写的 ystdex::function 直接性能上都能把 libstdc++ 摁在地上锤 除了实现难度比较大,而且吐槽了新议程中的试图把信号槽系统搬进标准c++的事情

当我们尝试做一个复杂的计算器时,会运用到非常多的计算功能,这些计算功能就是通过函数实现的,所以如何保存一个函数,就显得非常重要了,c++是一个静态语言,也不支持反射,所以不可能通过生成代码的方式生成一个函数,如果尝试诸如 lua、python 等语言的话,可以尝试一下这种方式
函数表

int add(int i, int j) { return i + j; }
auto mod = [] (int i, int j) -> int { return i + j; }
struct divide {
    int operator() (int denominator, int divisor) {
        return denominator / divisor;
    }
}
std::map<std::string, int(*)(int,int)> binops;
binops.insert ({"+", add});  // 将 add 函数和 + 绑定在一起

但是我们不能将 mod 和 divide 存入 binops,其中 lambda 有自己的类类型,与函数指针类型不匹配。解决办法是……std::function

std::function<int(int, int)> f1 = add;
std::function<int(int, int)> f2 = divide();
std::function<int(int, int)> f3 = [] (int i, int j) -> int { return i * j; };

但是,std::function 会面临重载函数二义性的问题,因为赋值的时候只提供了一个关于函数的名字,但是没有任何参数,编译器无法推断此时应当使用哪一个函数存入 std::function

第 15 章 面向对象程序设计

面向对象的介绍

这个地方其实我自己也不知道应当如何介绍,只能抄一点书上的内容了
P525-576

面向对象的核心是数据抽象、继承和动态绑定

基类派生Fork(也称为父类,子类)
派生类需要通过在类派生列表中明确指出它是从哪(些)个基类派生而得到的

虚函数使得派生类可以修改继承得到的那些标记为虚函数的函数,使之表现出不同的行为

动态绑定,运行时绑定,这个概念很奇怪,总之就是在代码运行的时候对不同的对象使用不同的成员函数

面向对象的使用

定义基类
virtual
override
定义派生类

阻止继承
final

虚函数

抽象基类:只含有纯虚函数的基类

访问控制

在之前第六章的时候我们讨论过一些访问控制,这里更加深入地去了解他们
public
protected
private
friend

其他类的操作

拷贝,赋值,移动,构造,析构……与先前的语法一致,但是可能会有一些不同

第 16 章 模板与泛型编程

定义一个模板

函数模板

template <typename T>
int compare (const T& v1, const T& v2) {
    return v1<v2?-1:v2<v1?1:0;
}

其实上面的代码实现了一个三相比较符,这个在c++20中以及被引入
我感觉如果接住了重载运算符的功能,就是用户自己添加运算符应当成为一个符合标准的事情才对

模板的特殊操作

template <unsigned N, unsigned M>
int compare (const char (&p1)[N], const char (&p2)[M]) {
    return strcmp (p1, p2);
}

往函数中传入了一个数组!!!
这都归功于模板的实例化,编译器在编译器就将数据的大小用我们看不到的方式传入了函数之中,让函数也直接得到了数组的大小

我们也可以使用 constexpr 对函数修饰要求能够在编译期返回一个常量结果

模板的保存总是又臭又长,因为其实例化展开的过程非常**,经常会让保存变得难以看懂,尤其是标准库那互相依赖Dependencies一报上百个的报错

类模板

template <typename T>
class Foo {

};

类模板其实和函数模板没有太大的区别,但是类模板需要手动指定类型实现实例化。类模板也存在偏特化和全特化,对视直接在类中写入对于什么样的类型执行什么样的操作。
比如 vector 和 map 创建一个对象的时候

在类外使用类模板名

template <typename T>
Foo<T> Foo<T>::operator+ (T a, T b) {
    // ...
}

一对一友元类,
通用和特定友好关系

令模板中的类型为自己的友元

模板类型别名

using strFoo = Foo<std::string>;

static 成员
每一个实例化的类都有自己对应的静态成员

模板参数的作用域

使用类的类型成员
这里可以看看之前我们是如何声明容器的迭代器的,那样子我们对于模板类的类型成员也会有所感觉了

模板类的默认模板形参

template <class T = int>
class Foo {
    // ...
}

类成员函数模板
其实本质上和函数模板并无区别,无非就是身在类中

实例化与成员函数

实例化
控制实例化

extern template class Blob<string>;
template int compare (const int&, const int&);

运行时绑定删除器
编译时绑定删除器

模板类型实参推断

类型转换与类型模板参数

template <typename T1, typename T2, typename T3>
T1 sum (T2, T3);

auto val3 = sum<long long>(i, lng);
// long long sum (int, long)

尾置返回类型

标准库中的类型转换模板

函数指针和实参推断

模板实参推断和引用
主要是关于引用折叠和右值引用的参数相关的内容

理解 std::move
std::move 的定义

template <typename T>
typename remove_reference<T>::type&& move (T&& t) {
    return static_cast<typename remove_reference<T>::type&&> (t);
}
std::string s1("hi"), s2;
s2 = std::move (std::string ("HELLO"));
s2 = std::move (s1);

从一个左值 static_cast 到一个右值是允许的

转发 std::forward

重载与模板

可变参数模板

模板参数包,函数参数包
我们使用一个省略号表示一个包,但是这个省略号实际上是由三个句号构成的,不是中文的省略号
使用 sizeof... 可以获取包的长度

template <typename T, typename... Args>
void foo (const T &t, const Args&... rest);

编写可变参数函数模板
包扩展
c++11 中引入的包,使得解包可以较为方便地通过递归的方式实现

template <typename T, typename... Args>
ostream& print (ostream& os, const T& t, const Args&... rest) {
    os << t << ",";
    return print (os, rest...);
}

转发参数包

模板特例化

第 IV 部分 高级主题

第 17 章 标准库特殊设施

认识 std::tuple

tuple 类似于 pairs,但是与 pairs 想不不同的是,pairs 只能存有两个(一对)类型,但是 tuple 可以存储若干个类型,所以这个类,在很多情况下也被用作函数返回值(与 struct 非常相近是不是?)

再会 std::bitset

其实 bitset 类,在这本书的开头我们就已经看到过了,现在是郑重其事地介绍一遍

可以理解为二进制数组,类似于java的bitmap吧,可以用来存储而静止图像

一个无限长度的整数类,也有支持的对应的运算符

初遇正则表达式

正则表达式才是真正的大头,这个功能真的是非常非常有用

头文件 regex

组件们

名称 介绍
regex 表示正则表达式的类
regex_match 进行正则匹配
regex_search 寻找第一个与表达式匹配的子序列
regex_replace 使用给定正则替换目标序列
sregex_iterator 调用regex_match匹配string中的所有匹配子序列
smatch 容器类,保存搜索结果
ssub_match string中匹配子表达式的结果

我们将在之后的时间里,详细地补充 regex 的使用方法,对于如何书写一个正则表达式,也会在届时详细补充

子表达式

随机数

在出现专门的随机数库之前,我们使用 cstdlib 提供的 rand 和 srand 生成随机数,但是 rand 返回的随机数结果是有范围,而且属于平均的随机,而且生成随机数的质量并不是很高,但是胜在速度足够快

随机数引擎

使用随机数引擎,我们甚至可以生成符合正态分布的随机数

再探 IO 库

我们讲了很多关于流的操作,但是我们没有将如何控制一个流。
但实际上,这个部分可以放弃了,在实际使用中,真的用的非常少,大家宁愿使用 printf, sprintf,也不会去使用 ostream 或者 stringstream 的格式化,因为真的是又臭又长,但是好处是处理效率很快,但是相比需要关心这个狗屁格式,显然是使用 c++20 引入的 format 库更加实用有效。

单字节操作
is.get, os.put, is.putback, is.unget, is.peek
多字节操作
is.get, is.getline, is.read, is.gcount, os.write, is.ignore

流随机访问
seek 和 tell 函数

第 18 章 用于大型程序的工具

异常处理

抛出异常

如何抛出一个异常其实还是一个非常富有技术含量的活

terminate 函数用于终止程序的运行,

如果代码写的足够多了,你经常可以发现自己的程序被杀死了,输出一条结果 xxx terminate: xxxx,大多数情况下是因为指针的问题,这个致命的异常要是一直没有被捕获,就会返回到最外层,然后调用 terminate 函数,终止程序的运行

捕获异常

noexcept

这个是不抛出异常,在我们之前使用 function 的时候也见到过

命名空间

命名空间的定义,使用,嵌套定义,分块定义,内联,无名,模板特例化,

调用命名空间内的成员

using 的使用

类、命名空间和作用域

多重继承和虚继承

第 19 章 特殊工具与技术

控制内存分配

当当,new 和 delete 相关的重载在这里出现了,我之前完全没有发现重载运算符那里没有讲new 和 delete,对了,delete 操作也是需要添加 noexcept 的修饰符的

c 中使用 malloc free 来分配释放内存,C++ 也继承了这部分功能

运行时类型识别(RTTI)

dynamic_cast

typeid 运算符,可以获取类型并返回 type_info

但是,其实 typeid 是一个非常慢的操作,我之前用这个玩意儿,还被大佬吐槽了一番,

枚举类型

C++11 引入了限定作用域的枚举类型,也让枚举拥有了更多的类型

枚举类型、联合体、结构体,这个在 c 中是作为一个基础的数据结构,在很早的时候就会被介绍到的,但是这里为了防止我们书写不那么 c++ 风格的代码,从而延后了。

限定枚举的作用域,在c中,枚举是在整个作用域可见的,导致枚举的名字不能重复,或者重复的意义可能出现不同,从而导致问题
使用 class 和 struct 表明枚举的范围

enum T { Tname1, Tname2, Tname3, Tname4 };
enum class CT { a, b, c, d };
Tname1  // ok
a       // not ok
CT::a   // ok

我们还可以限定枚举中元素的类型,默认为 int,

enum class intValues : unsigned long long {
    charTyp = 255, shortTyp = 65535, intTyp = 65535,
    // 这里出现了一个非常有趣的事情,int 的上限居然和 short 一样
    // 这个是因为标准没有明确规定int的具体长度,只规定了一个范围
    longType = 4294967295UL, long_longType = 18446744073709551615ULL
};

枚举类型的前置声明

enum class intValues : unsigned long long;

枚举的形参匹配

只能是枚举,就算值和枚举一样,对应的还是枚举,而非这个值

类成员指针

数据成员指针

const std::string CLASS::* pdata;
// 指向 CLASS 对象的 std::string 成员 的 指针

成员函数指针

char (CLASS::* pmf2) (CLASS::X, CLASS::y) const;
// 指针叫 pmf2,指向来自 CLASS 的函数
// 函数返回值为 char, 不能再函数内部修改变量的值
// 传入参数为 CLASS::X 和 CLASS::Y
using c = char (CLASS::*)(CLASS::X,CLASS::Y) const;
// 使用别名
c pmf3;

成员指针函数表
将成员函数作为可调用对象

嵌套类

只是如此嵌套而已,似乎并没有什么可以多讲的

局部类

和嵌套类也很相似,但是没有什么特别的不同,我把它提前放置在这里

联合体:union

特殊的类,将多种数据结构在一个空间上存储,实现类型的动态变化
union 用于实现了 lua的动态类型

c++11更新之后,它就变成一种特殊的类,拥有了权限控制,默认都是 public

union UT{
    char cval;
    int ival;
    long long llval;
    double dval;
    std::string sval;
};
std::map <std::string, UT> valueStack;

同枚举,类,结构体一样,联合体是可以匿名的

我们可以使用枚举存储当前联合体中存储的类型,在进行操作时加以判断

C++ 的固有不可移植性

因为 c++ 为了高效,需要编译到机器码,机器码则与对应的硬件设施相关,而其调用的库则是平台相关,导致c++的移植总是需要重新编译一串代码

位域

volatile
volatile 要求编译器不要对这个变量以及相关的进行优化,因为在多线程下,如果某一段代码被优化了,另一个线程对其的修改其实就不能生效了,这就会导致一定的问题,具体的可以看到 https://www.zhihu.com/question/31459750/answer/52061391 。书中对其描写非常之少

extern "C"
让链接器使用其他语言的编译器编译其中的代码,但是得让这个代码和c++能够一起运行

附录 A 标准库

A.1 标准库名字和头文件

A.2 算法概览

find (beg, end, val)
find_if (beg, end, unaryPred)
find_if_not (beg, end, unaryPred)
count (beg, end, val)
count_if (beg, end, unaryPred)

all_of (beg, end, unaryPred)
any_of (beg, end, unaryPred)
none_of (beg, end, unaryPred)

A.3 随机数

索引Index

tinyproxy 的部署和使用不完全指北

tinyproxy - GitHub

介绍

  • Tinyproxy 是一个面向 POSIX 系统开发的轻量级的开源 HTTP/HTTPS 代理守护进程Progress,其设计目标是快而小,从底层开始进行设计,保证了在高速的同时体积依然很小。它适用于需要完整 HTTP 代理特性,但系统资源又不足以运行大型代理的场景,比如嵌入式部署。
  • Tinyproxy 对小规模网络Network非常有用,这样的场合下大型代理会使系统资源紧张,或有安全风险。 Tinyproxy 的一个关键特性是其缓冲连接的理念。从效果上看, Tinyproxy服务Services器的响应进行了高速缓冲,然后按照客户端能够处理的最高速度进行响应。该特性极大的降低了网络延滞带来的问题。

  • 支持匿名模式:
    • 可让您配置哪些 HTTP标头可被允许通过,哪些 HTTP标头应被阻止。 这使您既可以限制从HTTP服务器向Web浏览器发送的数据(例如Cookie),也可以限制从Web浏览器到HTTP服务器发送的数据(例如版本信息)。
  • 支持 HTTPS:
    • 可以通过 CONNECT 请求Request来转发 HTTPS 连接。Tinyproxy 允许进行 HTTPS 连接的转发,而且无需通过 CONNECT 方法以任何形式修改流量内容(请参阅ConnectPort 指令)。
    • 使用 AddHeader 指令,您可以向传出的流量里添加或插入 HTTP 标头信息。
  • 远程监视:
    • 通过使用远程监视工具,您可以远程获知代理统计信息、日志和访问信息,从而确切了解代理的繁忙程度。
  • 负载监视:
    • 可配置成当负载达到某个程度时,拒绝新的代理请求。
  • 访问控制:
    • 可设置特定的 IP 地址或者 IP 段才可访问。
  • 安全:
    • 不需要 root 权限。
    • 只需进行一些配置(比如,将 Tinyproxy 创建的文件设为由非 root 用户拥有,并让它在大于 1024 的端口上运行),就能让 Tinyproxy 在没有任何特殊权限的情况下运行,从而将系统受到破坏的风险降至最低。
    • 您可以配置 Tinyproxy 来实现 访问控制,从而仅允许来自特定子网或特定接口Interface的请求,从而确保那些随机Random出现的、未经授权的人不能使用您的代理。
  • 轻量化:
    • 只需要极小的系统资源。
    • Tinyproxy 的体积很小,也仅需少量系统资源。在使用 glibc 时,内存占用一般大约 2 MB,而 CPU 占用会随着连接数量线性增长(具体取决于连接速度)。因此,Tinyproxy 可用在比较老旧的计算机上,或者用在基于 Linux 的路由器等网络设备上,而不会对设备性能造成明显的影响。
  • 支持基于 URL 的过滤。
  • 支持透明代理
    • 使客户端无需进行任何配置即可使用代理。您也可以将其用作您网站的反向代理前端。
  • 支持多级代理。
  • 开源:
    • Tinyproxy 采用 GNU GPL 协议(版本2及以上)分发。
  • 易于构建:
    • Tinyproxy 的构建和运行仅需一个最小化的 POSIX 环境。同时,它可以用其他附加库来添加额外功能。
  • 稳定:
    • 它的设计致力于防止缓冲区溢出。代码结构的简单也确保了此类bug易于定位。
  • 高自由度:
    • 如果您要构建自定义Web代理,可以轻松地修改 Tinyproxy 以满足您的自定义需求。 源码结构非常简单,遵循 KISS 原则。 因此,可以把它作为基础Base,来实现您可能需要Web代理执行的任何操作。

安装

直接安装

通过下面的命令在不同平台上直接安装 tinyproxy 服务
[RHEL](Red Hat Enterprise Linux) / CentOS / Fedora

yum install tinyproxy

从 EPEL 仓库Repository中安装 Tinyproxy。
Debian / Ubuntu

apt install tinyproxy

Archlinux / manjaro

pacman -S tinyproxy

openSUSE

zypper in tinyproxy

FreeBSD,OpenBSD 或 NetBSD

可以使用 pkg_add 程序来安装

pkg install tinyproxy

Mac OS
对于 Mac OS X 用户,可以查看 MacPorts 来检查其中的 Tinyproxy 端口是否已是最新版本。
如果您觉得您操作系统Operating System中的 Tinyproxy 二进制软件包不是最新的,请联系该操作系统的软件包维护者。 如果这行不通,您也可以从源代码编译Compiler得到最新的稳定版本。

homebrew install proxy

手动安装

最新版下载链接:

https://github.com/tinyproxy/tinyproxy/releases/latest

编译安装

我们以源代码的形式分发 Tinyproxy,必须在对其编译后,才能在您的系统上使用。 请查看源代码树中的 INSTALL 文件,获取构建说明。 Tinyproxy 的最新稳定发行版Distribution本是 tinyproxy-1.8.4.tar.bz2, 它发布于2016年1月1日。Tinyproxy 1.8.4 的 NEWS 文件包含了发行说明。您可以使用其PGP签名验证tarball。 您也可以浏览 Tinyproxy 旧的发行版本。
通过
我们使用 Git 作为 Tinyproxy 源代码仓库的版本控制系统。 要获取 Tinyproxy 仓库的副本,请使用以下命令:

git clone --depth=1 git@github.com:tinyproxy/tinyproxy.git

下载最新版本代码 (当然,可能是实验性代码,建议还是从上面的链接手动下载最新版 release 代码)
如果你使用的是其它平台,更多的安装方式可直接参考官方文档:https://tinyproxy.github.io/

  1. 通过源码安装

如果你使用的平台,官方还不支持通过软件包安装。你也可以通过源码进行编译安装。

git clone --depth=1 https://github.com/tinyproxy/tinyproxy.git
cd tinyproxy
./autogen.sh
./configure
make
make install

配置

TinyProxy 默认配置文件Profile路径为 /etc/tinyproxy/tinyproxy.conf。如果你要自定义配置文件位置,可以在启动 TinyProxy 时 通过 -c 参数Parameter来指定。

该页面描述配置文件的语法和内容。

Tinyproxy 配置文件包含很多键值对,每行一对。以 # 开头的行或者空白行是注释,会被忽略。关键字不区分大小写,但值内容会区分大小写。如果值内容里包含空格,可以将他们括在双引号"中。可用的关键字及其描述如下:

快速查看当前配置文件

cat /etc/tinyproxy/tinyproxy.conf | grep -v '#' | grep -v '^$'

打开配置文件

vim /etc/tinyproxy/tinyproxy.conf
下面我们来看下几个主要的配置参数: 变量 默认值 解释
User tinyproxy 指定运行 TinyProxy 的用户。可以填用户名或UID。
Group tinyproxy 指定运行 TinyProxy 的用户组。可以填用户名或UID。
Port 8888 指定 TinyProxy 的监听端口。Tinyproxy服务将侦听的端口。如果端口小于 1024,则需要以 root 用户的身份启动 Tinyproxy 进程。
Listen 指定 TinyProxy 绑定的网卡接口,默认是绑定到所有可用的网卡接口的。如需绑定到指定网卡接口,只需去掉对应的注释并指定网卡对应 IP 地址即可。在默认情况下,Tinyproxy 会侦听在所有可用接口上的连接(即,侦听地址 0.0.0.0)。通过配置此参数,Tinyproxy 可以被告知仅侦听一个特定地址。
Allow 127.0.0.1 指定可访问 TinyProxy 设备的 IP 或网段,默认仅允许本机访问。如果你想允许所有人使用该代理,注释 Allow 选项即可。如果你想增加多个可访问的网段,可以用多个 Allow 选项同时定义不同网段即可。
Bind 这允许您指定 Tinyproxy 将绑定到哪个目标地址,以将其连接到Web服务器或上游代理。
BindSame 在多网卡的情况下,设置出口 IP 是否与入口 IP 相同。默认情况下是关闭的。
例如:服务器上存在 IP 1.2.3.4,当你请求该 IP 对应的 Tinyproxy 代理时,也通过 1.2.3.4 做为出口访问目标网站。如果此布尔值选项被设置为 Yes,Tinyproxy 会将目标地址设定为触发了传出请求的传入连接的IP地址。
Timeout 600 Tinyproxy关闭连接之前,允许该连接处于非活动状态的最大秒数。
StartServers 10 指定 TinyProxy 初始启动的子进程数量。一般应将其设置为位于 MinSpareServers和 MaxSpareServers 之间的值。
MinSpareServers, MaxSpareServers 5, 20 Tinyproxy 始终保留一定数量的空闲子进程,以保证它可以快速处理新传入的客户端请求。 MinSpareServer 和 MaxSpareServers 控制备用进程数的最大值和最小值。即,当备用服务器的数量降至 MinSpareServers以下时,Tinyproxy 将在后台开始创建新的备用进程,而当备用进程的数量超过 MaxSpareServers 时,Tinyproxy 将杀死多余的进程
MaxClients 100 设置最大客户端链接数
MaxRequestsPerChild 此选项限制一个子进程在终止之前将会处理的最大连接数。 默认值为0,即无限制。此选项是在出现内存泄漏问题时可采取的紧急措施。 在这种情况下,请将 MaxRequestsPerChild 设置为例如1000 或 10000,可能会有用。
Upstream,No Upstream 该选项允许您设置一组基于所访问站点主机Host或域的规则,来选择是否使用某些上游代理服务器。这些规则按照在配置文件中录入的顺序存储,并且在使用时 最后一条 所匹配的规则会生效。有以下三种指定上游主机的方式:

upstream host:port 开启一个常规形式的上游代理
upstream host:port "site_spec" 为匹配 site_spec 的站点启用上游代理
no upstream "site_spec" 为匹配 site_spec 的站点禁用上游代理

匹配可以用主机名、域名、IP范围等形式来指定:
.name 匹配任何在域 name 中的主机
. 匹配任何无域名的主机(在空域名中)
IP/bits 匹配IP地址/掩码位数
IP/mask 匹配IP地址/掩码
Allow, Deny Allow和Deny选项用于自定义允许哪些客户端访问Tinyproxy。 Allow和Deny行可以在配置文件里重复出现,以构建Tinyproxy的访问控制列表。在配置文件中的顺序很重要。 如果没有 Allow 或 Deny 行,则允许任意的客户端。反之,默认的操作是拒绝访问。 允许或拒绝的参数可以配置为客户端主机的单个IP地址,例如 127.0.0.1,IP地址范围,例如 192.168.0.1/24,或将与客户端主机名尾端匹配的字符串, 可以是完整的主机名,例如 host.example.com,或域名,例如.example.com,或者顶级域名,例如.com。
XTinyproxy 将此选项设置为Yes将通知Tinyproxy将包含客户端IP地址的标头X-Tinyproxy添加到请求中。

进阶选项

Filter /etc/ tinyproxy/ filter 指定设置过滤内容文件的位置
FilterURLs Off/On 设置使用 URL 或是域名方式进行过滤,默认是基于 URL 方式过滤的。域名过滤只检查域名段,URL 过滤则检查整个 URL。
FilterExtended Off/On 设置使用 POSIX 基本或者扩展的正则表达式来匹配过滤规则,默认为使用基本的
FilterCaseSensitive Off/On 设置是否使用区分大小写的正则表达式,默认为不区分大小写。
FilterDefaultDeny Yes/No 设置默认过滤策略。如果将该指令注释掉或设为 No,过滤规则为禁止访问规则。该值默认为 Yes,过滤规则为只允许访问过滤文件中的地址。
过滤规则配置示例:

1. 在 /etc/tinyproxy/filter 文件中添加代理允许或拒绝的域名地址。

hi-linux.com
过滤文件中的域名地址也是支持正则表达式的。

\.google\.com$</br>^hi-linux\.com$
2. 仅允许代理请求 hi-linux.com 的内容,配置如下:

Filter "/etc/tinyproxy/filter"
FilterURLs On
FilterDefaultDeny Yes
3. 仅允许代理请求除 hi-linux.com 域名以外的内容,
置如下:

Filter "/etc/tinyproxy/filter"
FilterURLs On
FilterDefaultDeny No
DefaultErrorFile "/usr/share/ tinyproxy/ default.html" 该选项控制如果发生未配置的错误时,返回的HTML模板文件。
ErrorFile 该选项控制 Tinyproxy 如果遇到特定的HTTP错误时,会返回哪个HTML文件。它包含两个参数,错误号、HTML错误文件的路径。
StatHost 该选项配置被当作统计主机的主机名或者IP地址:每当收到对该主机的请求时,Tinyproxy 会返回内部统计信息页面,而不会将请求转发给该主机。此页面的模板可以使用 StatFile 配置项进行配置。StatHost 的默认值为 tinyproxy.stats。
StatFile "/usr/share/ tinyproxy/ stats.html" 该选项配置 Tinyproxy 在收到对统计主机的请求时发送的HTML文件。如果未设置此选项,Tinyproxy 将返回一个硬编码的基本统计信息页面。有关详细信息,请参见 tinyproxy(8) 手册页中的 STATHOST 部分。需要注意的是,使用StatFile 以及 ErrorFile 和 DefaultErrorFile 选项配置的错误文件都是模板文件,其中可以包含一些模板变量,并由 Tinyproxy 在发送时进行扩展。例如,"{cause}" 表示简短的错误描述,"{detail}"表示详细的错误消息。tinyproxy(8) 手册页包含所有模板变量的描述。
LogLevel Info 设置日志级别。高于或等于该设置项级别的日志消息会被记录。例如,如果LogLevel设为 Warning,则从Warning到Critical级别的所有日志消息会被输出,但Notice或更低等级的日志消息会被过滤掉。允许的取值如下:

Critical(最简洁)
Error
Warning
Notice
Connect(不包含Info的连接日志)
Info(最详细)
LogFile /var/log/ tinyproxy/ tinyproxy.log 指定日志文件位置。该选项控制 Tinyproxy 将调试输出写入文件的位置。 此外,Tinyproxy 可以将日志输出到syslog - 请参阅 Syslog 选项。
Syslog Off/On 指定 TinyProxy 是否开启 Syslog 来记录日志
注:Logfile 和 Syslog 只能同时启用一个。如果两个都不启用的话 TinyProxy 会将日志直接输出到终端Terminal的标准输出。当设为 On 时,此选项告知 Tinyproxy 将其调试消息写入syslog,而不是写入由 LogFile 所配置的日志文件。这两个选项是互斥的。
PidFile /var/run/ tinyproxy/ tinyproxy.pid 指定 Pid 文件位置, 在 PidFile 文件不存在时会运行失败。该选项控制 Tinyproxy 主进程将其进程 ID 号存入的文件路径,用于处理信号。
DisableViaHeader 关闭 指定是否在 Header 中显示 Tinyproxy 相关信息,默认是关闭的。如果开启将不会在 Header 中显示 Tinyproxy 相关信息,相当于 Tinyproxy 是隐身模式
AddHeader 配置一个或多个 HTTP 请求标头,用于添加到由 Tinyproxy 发出的 HTTP 请求中。 需要注意的是,此选项不适用于 HTTPS 流量,因为 Tinyproxy 无法控制要交换的标头。
AddHeader "X-My-Header" "Powered by Tinyproxy"
ViaProxyName "tinyproxy" RFC 2616 要求代理将 Via 标头添加到 HTTP 请求中,但使用真实主机名可能会引起安全问题。 如果设置了 ViaProxyName 选项,其字符串值将用作 Via 标头中的主机名。 否则,将使用服务器的主机名。
DisableViaHeader No/Yes 该选项设置为 yes 时,Tinyproxy 不会将 Via 标头添加到请求中。 这实际上就使 Tinyproxy 进入了隐身模式。请注意,RFC 2616 要求代理设置 Via 头,因此启用此选项会破坏合规性。 除非您知道自己在做什么,否则不要禁用 Via 标头...
Filter Tinyproxy 支持基于 URL 或域的网站过滤。 此选项指定包含过滤规则的文件的位置,每行一条规则。
FilterURLs 如果此布尔选项设置为 Yes 或 On,则根据 URL 执行过滤,而不根据域执行过滤。默认设置为根据域进行过滤。
FilterExtended 如果此布尔选项设置为 Yes,则扩展的 POSIX 正则表达式将用来匹配过滤器规则。默认使用基本 POSIX 正则表达式。
FilterCaseSensitive 如果此布尔选项设置为 Yes,则过滤器规则匹配时会区分大小写。 默认匹配时不区分大小写。
FilterDefaultDeny 默认的过滤策略会允许所有与过滤规则不匹配的内容。 将 FilterDefaultDeny 设置为 Yes 会更改该策略,从而拒绝在过滤规则所匹配的域或URL之外的任何内容。
Anonymous 如果设置了 Anonymous 选项,则启用匿名代理。由 Anonymous 配置的标头被允许通过,其他标头会被拒绝。 如果未配置 Anonymous 选项,则允许所有标头通过。您必须给标头内容加上引号。
大多数站点都需要启用 cookie 才能正常工作,因此,如果您访问这种站点,需要允许 cookie 通过。
用法举例:

Anonymous "Host"
Anonymous "Authorization"
Anonymous "Cookie"
ConnectPort 443,563 此选项用于指定 CONNECT 方法所允许的端口。 如果找不到 ConnectPort 行,则允许所有端口。若要完全禁用 CONNECT,请仅设置一条值为 0 的 ConnectPort 选项行。
ReversePath 配置一条或多条 ReversePath 选项,以启用反向代理支持。 使用反向代理,可以使许多站点看起来像是单个站点的一部分。
配置以下指令,并在自己的计算机上的端口 8888上运行 Tinyproxy,则可以通过 http://localhost:8888/example/来访问站点 example.com。
ReversePath "/example/" "http://www.example.com/"
ReverseOnly 当把 Tinyproxy 用作反向代理时,强烈建议将此布尔选项设置为 Yes ,从而关闭普通代理功能。
ReverseMagic 将此选项设置为Yes,可让 Tinyproxy 使用 cookie 来跟踪反向代理的映射。 如果您需要反向具有绝对链接的代理站点,必须启用此选项。
ReverseBaseURL 用于访问此反向代理的URL地址。 该 URL 会被用于重写 HTTP 重定向地址,以使它们不会绕过代理。 如果您有一连串的反向代理,则需要在此处放置最外层的 URL(也就是终端用户在其浏览器中键入的地址)。如果未设置此选项,则不会修改重定向。

运行和测试

  • 运行 TinyProxy 非常简单,使用官方提供的脚本Script即可。
# 启动 TinyProxy
service tinyproxy start

# 停止 TinyProxy
service tinyproxy stop

# 重启 TinyProxy
service tinyproxy restart
  • 如果服务器有启用防火墙,记得开放相应的 TinyProxy 端口
    iptables -I INPUT -p tcp –dport 8888 -j ACCEPT
  • 测试代理是否正常工作

使用curl命令测试代理服务器是否生效

curl url --proxy <代理服务器域名或 IP>:<监听端口>

如果是https代理加 -k 参数

curl url --proxy <代理服务器域名或 IP>:<监听端口> -k

例如

curl url --proxy 8.8.8.8:8888

或使用 telnet 命令

telent <代理服务器域名或 IP> 8888
  • 查看 TinyProxy 请求日志
tail -f /var/log/tinyproxy/tinyproxy.log

FAQ

time out(超时)可能是因为服务器的防火墙没有打开这个端口
Ubuntu 防火墙操作看[这篇]()
连接上了但是被 reject(拒绝)了可能是因为 tinyproxy 没有允许你正在使用的这台电脑或是服务器的 ip

firewall-cmd --add-port=8888/tcp --permanent
firewall-cmd --reload

uwp

iptables

支持

您可以随时通过 github issues 来报告新 bug,或提出功能建议。
Tinyproxy 的开发者们也会浏览在 irc.freenode.net 上的 #tinyproxy 标签Tag

实践

[如何连接代理服务器]()

qqbot 代理文件服务下载

代理访问掩藏真实 ip

参考

https://tinyproxy.github.io/
https://github.com/tinyproxy/tinyproxy
https://www.cnblogs.com/daoyi/p/bu-shutinyproxy-tou-ming-dai-li-fu-wu.html
https://blog.csdn.net/testcs_dn/article/details/89335426
https://java-er.com/blog/tinyproxy-install/
https://www.cnblogs.com/masako/p/11459496.html
https://cloud.tencent.com/developer/article/1475747
https://www.jianshu.com/p/cfef3e491b52
https://www.jianshu.com/p/0f8ac701748d
https://www.cnblogs.com/masako/p/11459496.html
https://zhuanlan.zhihu.com/p/72903589

程序员升职记

Human Resource Machine

https://steamcommunity.com/sharedfiles/filedetails/?id=550015574
收发室

    INBOX   
    OUTBOX  
    INBOX   
    OUTBOX  
    INBOX   
    OUTBOX  

繁忙的收发室

a:  INBOX   
    OUTBOX  
    JUMP     a
a:  INBOX   
    OUTBOX  
    INBOX   
    OUTBOX  
    JUMP     a

复印楼层

    COPYFROM 4
    OUTBOX  
    COPYFROM 0
    OUTBOX  
    COPYFROM 3
    OUTBOX  

扰码处理器

a:  INBOX   
    COPYTO   0
    INBOX   
    OUTBOX  
    COPYFROM 0
    OUTBOX  
    JUMP     a

咖啡时间
多雨之夏

a:  INBOX   
    COPYTO   0
    INBOX   
    ADD      0
    OUTBOX  
    JUMP     a

零扑灭运动

a:
b:  INBOX   
    JUMPZ    b
    OUTBOX  
    JUMP     a

三倍扩大室

a:  INBOX   
    COPYTO   0
    ADD      0
    ADD      0
    OUTBOX  
    JUMP     a

零保护行动

    JUMP     b
a:  OUTBOX  
b:
c:  INBOX   
    JUMPZ    a
    JUMP     c

八倍扩大装置

a:  INBOX   
    COPYTO   0
    ADD      0
    COPYTO   0
    ADD      0
    COPYTO   0
    ADD      0
    OUTBOX  
    JUMP     a

加运算走廊

a:  INBOX   
    COPYTO   0
    INBOX   
    COPYTO   1
    SUB      0
    OUTBOX  
    COPYFROM 0
    SUB      1
    OUTBOX  
    JUMP     a

四十倍扩大器

a:  INBOX   
    COPYTO   0
    ADD      0
    COPYTO   0
    ADD      0
    COPYTO   0
    ADD      0
    COPYTO   0
    ADD      0
    COPYTO   1
    ADD      0
    ADD      1
    OUTBOX  
    JUMP     a

均衡之间

    JUMP     b
a:  COPYFROM 0
    OUTBOX  
b:
c:  INBOX   
    COPYTO   0
    INBOX   
    SUB      0
    JUMPZ    a
    JUMP     c

最大值室

    JUMP     c
a:  COPYFROM 0
b:  OUTBOX  
c:  INBOX   
    COPYTO   0
    INBOX   
    SUB      0
    JUMPN    a
    ADD      0
    JUMP     b

斗志注入
绝对正能量

    JUMP     c
a:  COPYTO   0
    SUB      0
    SUB      0
b:  OUTBOX  
c:  INBOX   
    JUMPN    a
    JUMP     b

专属休息室

a:
b:  INBOX   
    JUMPN    d
    INBOX   
    JUMPN    e
c:  COPYFROM 4
    OUTBOX  
    JUMP     b
d:  INBOX   
    JUMPN    c
e:  COPYFROM 5
    OUTBOX  
    JUMP     a

海滩天堂
计时器

a:  INBOX   
    COPYTO   0
b:
c:  OUTBOX  
    BUMPDN   0
    JUMPN    d
    JUMP     c
d:  BUMPUP   0
    JUMPZ    a
    BUMPUP   0
    JUMP     b
    JUMP     d
a:
b:
c:  OUTBOX  
d:  INBOX   
    JUMPN    f
    JUMPZ    a
    COPYTO   0
    OUTBOX  
e:  BUMPDN   0
    JUMPZ    c
    OUTBOX  
    JUMP     e
f:  COPYTO   0
    OUTBOX  
g:  BUMPUP   0
    JUMPZ    b
    OUTBOX  
    JUMP     g

乘法研讨会

    JUMP     b
a:  COPYFROM 0
    OUTBOX  
b:  COPYFROM 9
    COPYTO   0
    INBOX   
    COPYTO   1
    INBOX   
    COPYTO   2
c:  JUMPZ    a
    COPYFROM 0
    ADD      1
    COPYTO   0
    BUMPDN   2
    JUMP     c
    BUMPUP   9
    JUMP     k
a:  ADD      1
b:  ADD      1
c:  ADD      1
d:  ADD      1
e:  ADD      1
f:  ADD      1
g:  ADD      1
h:  ADD      1
i:  ADD      1
j:  OUTBOX  
k:  INBOX   
    COPYTO   1
    INBOX   
    JUMPZ    j
    SUB      9
    JUMPZ    i
    SUB      9
    JUMPZ    h
    SUB      9
    JUMPZ    g
    SUB      9
    JUMPZ    f
    SUB      9
    JUMPZ    e
    SUB      9
    JUMPZ    d
    SUB      9
    JUMPZ    c
    SUB      9
    JUMPZ    b
    SUB      9
    JUMP     a

零结尾字符串

    JUMP     c
a:  ADD      0
b:  OUTBOX  
c:  INBOX   
    JUMPZ    b
d:  COPYTO   0
    INBOX   
    JUMPZ    a
    ADD      0
    JUMP     d

斐波那契参上

a:  INBOX   
    COPYTO   0
    COPYFROM 9
    COPYTO   1
    COPYTO   2
    BUMPUP   1
b:  ADD      2
    COPYTO   3
    COPYFROM 0
    SUB      1
    JUMPN    a
    COPYFROM 1
    OUTBOX  
    COPYFROM 1
    COPYTO   2
    COPYFROM 3
    COPYTO   1
    JUMP     b
a:  INBOX   
    COPYTO   0
    COPYFROM 9
    COPYTO   1
    COPYTO   2
    BUMPUP   1
    OUTBOX  
    COPYFROM 1
b:  ADD      2
    COPYTO   3
    COPYFROM 0
    SUB      3
    JUMPN    a
    COPYFROM 3
    OUTBOX  
    COPYFROM 1
    COPYTO   2
    COPYFROM 3
    COPYTO   1
    JUMP     b

最小的数字

    JUMP     b
a:  COPYFROM 0
    OUTBOX  
b:  INBOX   
    JUMP     d
c:  ADD      0
d:  COPYTO   0
e:  INBOX   
    JUMPZ    a
    SUB      0
    JUMPN    c
    JUMP     e

模运算

    JUMP     c
a:  ADD      1
b:  OUTBOX  
c:  INBOX   
    COPYTO   0
    INBOX   
    COPYTO   1
    COPYFROM 0
d:  SUB      1
    JUMPZ    b
    JUMPN    a
    JUMP     d

累加的倒计时

    JUMP     c
a:  COPYFROM 0
b:  OUTBOX  
c:  INBOX   
    JUMPZ    b
    COPYTO   1
d:  COPYTO   0
    BUMPDN   1
    JUMPZ    a
    ADD      0
    JUMP     d

小试除法

    JUMP     b
a:  COPYFROM 3
    OUTBOX  
b:  COPYFROM 9
    COPYTO   3
    INBOX   
    COPYTO   0
    INBOX   
    COPYTO   1
c:  COPYFROM 0
    SUB      1
    JUMPN    a
    COPYTO   0
    BUMPUP   3
    JUMP     c

午夜惊魂
三排序

    JUMP     c
a:  COPYFROM 1
    OUTBOX  
    COPYFROM 2
    OUTBOX  
    COPYFROM 3
    OUTBOX  
    JUMP     h
b:  COPYFROM 3
    OUTBOX  
    COPYFROM 2
    OUTBOX  
    COPYFROM 1
    OUTBOX  
c:
d:
e:
f:
g:
h:
    INBOX   
    COPYTO   1
    INBOX   
    COPYTO   3
    SUB      1
    JUMPN    i
    INBOX   
    COPYTO   2
    SUB      1
    JUMPN    k
    ADD      1
    SUB      3
    JUMPN    a
    COPYFROM 1
    OUTBOX  
    COPYFROM 3
    OUTBOX  
    COPYFROM 2
    OUTBOX  
    JUMP     g
i:  INBOX   
    COPYTO   2
    SUB      3
    JUMPN    j
    ADD      3
    SUB      1
    JUMPN    b
    COPYFROM 3
    OUTBOX  
    COPYFROM 1
    OUTBOX  
    COPYFROM 2
    OUTBOX  
    JUMP     f
j:  ADD      3
    OUTBOX  
    COPYFROM 3
    OUTBOX  
    COPYFROM 1
    OUTBOX  
    JUMP     e
k:  ADD      1
    OUTBOX  
    COPYFROM 1
    OUTBOX  
    COPYFROM 3
    OUTBOX  
    JUMP     d
a:  INBOX   
    COPYTO   0
    INBOX   
    COPYTO   2
    INBOX   
b:  COPYTO   1
    SUB      2
    JUMPN    c
    COPYTO   1
    ADD      2
    COPYTO   2
    SUB      1
    COPYTO   1
c:  COPYFROM 1
    SUB      0
    JUMPN    d
    COPYFROM 0
    OUTBOX  
    COPYFROM 1
    OUTBOX  
    COPYFROM 2
    OUTBOX  
    JUMP     a
d:  COPYTO   1
    ADD      0
    COPYTO   0
    SUB      1
    JUMP     b

存储楼层

a:  NBOX   
    COPYTO   12
    COPYFROM [12]
    OUTBOX  
    JUMP     a

串存储楼层

a:  INBOX   
    COPYTO   24
b:  COPYFROM [24]
    JUMPZ    a
    OUTBOX  
    BUMPUP   24
    JUMP     b

反转字符串

a:
b:  INBOX   
    JUMPZ    c
    COPYTO   [14]
    BUMPUP   14
    JUMP     b
c:
d:  BUMPDN   14
    COPYFROM [14]
    OUTBOX  
    COPYFROM 14
    JUMPZ    a
    JUMP     d

库存报告

    BUMPUP   14
    BUMPUP   14
    COPYTO   15
    ADD      14
    COPYTO   16
    JUMP     d
a:  ADD      16
b:  ADD      14
c:  OUTBOX  
d:  INBOX   
    SUB      4
    JUMPN    a
    JUMPZ    b
    BUMPUP   15
    JUMP     c

王五去哪
删除元音字母

    JUMP     b
a:  COPYFROM 9
    OUTBOX  
b:
c:  INBOX   
    COPYTO   9
    COPYFROM 5
    COPYTO   6
d:  COPYFROM [6]
    JUMPZ    a
    SUB      9
    JUMPZ    c
    BUMPUP   6
    JUMP     d

删除重复项

    INBOX   
    JUMP     b
a:  BUMPUP   14
    COPYFROM 12
b:  COPYTO   [14]
    OUTBOX  
c:  COPYFROM 14
    COPYTO   13
    INBOX   
    COPYTO   12
d:  SUB      [13]
    JUMPZ    c
    BUMPDN   13
    JUMPN    a
    COPYFROM 12
    JUMP     d

字母排序

a:  INBOX   
    COPYTO   [23]
    JUMPZ    b
    BUMPUP   23
    JUMP     a
b:  COPYTO   22
c:  INBOX   
    JUMPZ    h
    COPYTO   20
    SUB      [22]
    JUMPZ    g
    JUMPN    e
d:  COPYFROM [22]
    JUMPZ    i
    OUTBOX  
    BUMPUP   22
    JUMP     d
e:  COPYFROM 20
f:  OUTBOX  
    INBOX   
    JUMPZ    j
    JUMP     f
g:  COPYFROM 20
    OUTBOX  
    BUMPUP   22
    SUB      23
    JUMPN    c
h:
i:
j:

数据链

a:  INBOX   
b:  COPYTO   12
    COPYFROM [12]
    OUTBOX  
    BUMPUP   12
    COPYFROM [12]
    JUMPN    a
    JUMP     b

数位炸弹

-- HUMAN RESOURCE MACHINE PROGRAM --

    COPYFROM 11
    ADD      11
    COPYTO   8
    COPYFROM 10
    ADD      10
    COPYTO   7
    JUMP     e
a:  ADD      10
    JUMPN    b
    COPYTO   0
    BUMPUP   1
b:  COPYFROM 2
    JUMPZ    c
    OUTBOX  
c:  COPYFROM 1
    OUTBOX  
d:  COPYFROM 0
    OUTBOX  
e:  INBOX   
    COPYTO   0
    SUB      10
    JUMPN    d
    COPYFROM 9
    COPYTO   1
    COPYTO   2
f:  COPYFROM 0
    SUB      8
    JUMPN    g
    COPYTO   0
    BUMPUP   2
    BUMPUP   2
    JUMP     f
g:  ADD      11
    JUMPN    h
    COPYTO   0
    BUMPUP   2
h:
i:  COPYFROM 0
    SUB      7
    JUMPN    a
    COPYTO   0
    BUMPUP   1
    BUMPUP   1
    JUMP     i
-- HUMAN RESOURCE MACHINE PROGRAM --

a:  COPYFROM 9
    COPYTO   0
    COPYTO   1
    INBOX   
    COPYTO   8
    SUB      10
    JUMPN    g
    ADD      10
    SUB      11
    JUMPN    d
b:  COPYFROM 8
    SUB      11
    JUMPN    c
    COPYTO   8
    BUMPUP   0
    JUMP     b
c:  COPYFROM 0
    OUTBOX  
d:
e:  COPYFROM 8
    SUB      10
    JUMPN    f
    COPYTO   8
    BUMPUP   1
    JUMP     e
f:  COPYFROM 1
    OUTBOX  
g:  COPYFROM 8
    OUTBOX  
    JUMP     a

重设坐标

    JUMP     b
a:  ADD      15
    OUTBOX  
    COPYFROM 1
    OUTBOX  
b:  COPYFROM 14
    COPYTO   1
    INBOX   
c:  SUB      15
    JUMPN    a
    COPYTO   0
    BUMPUP   1
    COPYFROM 0
    JUMP     c

质数工厂

a:
b:  INBOX   
    COPYTO   10
    COPYFROM 24
    COPYTO   22
    BUMPUP   22
    COPYTO   1
c:  BUMPUP   1
d:  COPYFROM 24
    COPYTO   12
    SUB      10
e:  COPYTO   11
    BUMPUP   12
    COPYFROM 11
    ADD      1
    JUMPN    e
    JUMPZ    f
    BUMPDN   22
    JUMPZ    c
    COPYFROM 10
    OUTBOX  
    JUMP     b
f:  COPYFROM 1
    OUTBOX  
    BUMPDN   12
    JUMPZ    a
    BUMPUP   12
    COPYTO   10
    JUMP     d

排序楼层

    JUMP     b
a:  BUMPUP   24
b:  BUMPDN   24
c:
d:
e:  BUMPUP   24
    COPYTO   22
    COPYTO   21
    INBOX   
    JUMPZ    g
    COPYTO   20
    COPYTO   [22]
    BUMPDN   21
    JUMPN    c
f:  COPYFROM 20
    SUB      [21]
    JUMPN    e
    COPYFROM [21]
    COPYTO   [22]
    COPYFROM 20
    COPYTO   [21]
    BUMPDN   22
    BUMPDN   21
    JUMPN    d
    JUMP     f
g:
h:  BUMPDN   24
    JUMPN    a
    COPYFROM [24]
    OUTBOX  
    JUMP     h

最终程序

成就 1

INBOX   
COPYTO   0
INBOX   
COPYTO   1
SUB      0
OUTBOX  
COPYFROM 0
SUB      1
OUTBOX  
INBOX   
COPYTO   0
INBOX   
COPYTO   1
SUB      0
OUTBOX  
COPYFROM 0
SUB      1
OUTBOX  

INBOX   
COPYTO   0
INBOX   
COPYTO   1
SUB      0
OUTBOX  
COPYFROM 0
SUB      1
OUTBOX  

INBOX   
COPYTO   0
INBOX   
COPYTO   1
SUB      0
OUTBOX  
COPYFROM 0
SUB      1
OUTBOX  

OJ 必须要了解的算法(如果要和别人比时间的话)

1. 数据量巨大的时候 std::cin, std::cout TLE (超时)

static bool speedUP = [](){std::ios::sync_with_stdio(false); cin.tie(nullptr); return true}();
// 静态变量先于 main 函数初始化,以实现直接调用 lambda 函数
// std::ios::sync_with_stdio (false); 用以关闭 同步功能,不写缓存,不 flush
// cin.tie (nullptr); 用于绑定输入流,nullptr 为当前输入流
// 写了一个 lambda 函数并调用了

https://byvoid.com/zhs/blog/fast-readfile/
https://zhuanlan.zhihu.com/p/35652783
https://www.jianshu.com/p/fa8ad995d300

2. 快速整数平方根(牛顿迭代打表)(std::sqrt 的 4 倍)

inline int mysqrt(int n) {
    static int table[256] = {
        0,    16,  22,  27,  32,  35,  39,  42,  45,  48,  50,  53,  55,  57,
        59,   61,  64,  65,  67,  69,  71,  73,  75,  76,  78,  80,  81,  83,
        84,   86,  87,  89,  90,  91,  93,  94,  96,  97,  98,  99, 101, 102,
        103, 104, 106, 107, 108, 109, 110, 112, 113, 114, 115, 116, 117, 118,
        119, 120, 121, 122, 123, 124, 125, 126, 128, 128, 129, 130, 131, 132,
        133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 144, 145,
        146, 147, 148, 149, 150, 150, 151, 152, 153, 154, 155, 155, 156, 157,
        158, 159, 160, 160, 161, 162, 163, 163, 164, 165, 166, 167, 167, 168,
        169, 170, 170, 171, 172, 173, 173, 174, 175, 176, 176, 177, 178, 178,
        179, 180, 181, 181, 182, 183, 183, 184, 185, 185, 186, 187, 187, 188,
        189, 189, 190, 191, 192, 192, 193, 193, 194, 195, 195, 196, 197, 197,
        198, 199, 199, 200, 201, 201, 202, 203, 203, 204, 204, 205, 206, 206,
        207, 208, 208, 209, 209, 210, 211, 211, 212, 212, 213, 214, 214, 215,
        215, 216, 217, 217, 218, 218, 219, 219, 220, 221, 221, 222, 222, 223,
        224, 224, 225, 225, 226, 226, 227, 227, 228, 229, 229, 230, 230, 231,
        231, 232, 232, 233, 234, 234, 235, 235, 236, 236, 237, 237, 238, 238,
        239, 240, 240, 241, 241, 242, 242, 243, 243, 244, 244, 245, 245, 246,
        246, 247, 247, 248, 248, 249, 249, 250, 250, 251, 251, 252, 252, 253,
        253, 254, 254, 255
     };
    int xn;
    if (n >= 0x7FFEA810) return 0xB504;
    if (n >= 0x10000) {
        if (n >= 0x1000000) {
            if (n >= 0x10000000) {
                if (n >= 0x40000000) {
                     xn = table[n>> 24] << 8;
                 } else {
                     xn = table[n>> 22] << 7;
                 }
             } else {
                if (n>= 0x4000000) {
                     xn = table[n>> 20] << 6;
                 } else {
                     xn = table[n>> 18] << 5;
                 }
             }
             xn = (xn + 1 + (n/ xn)) >> 1;
             xn = (xn + 1 + (n/ xn)) >> 1;
            return ((xn * xn) > n) ? --xn : xn;
         } else {
            if (n>= 0x100000) {
                if (n>= 0x400000) {
                     xn = table[n>> 16] << 4;
                 } else {
                     xn = table[n>> 14] << 3;
                 }
             } else {
                if (n>= 0x40000) {
                     xn = table[n>> 12] << 2;
                 } else {
                     xn = table[n>> 10] << 1;
                 }
             }

             xn = (xn + 1 + (n/ xn)) >> 1;
            return ((xn * xn) > n) ? --xn : xn;
         }
     } else {
        if (n>= 0x100) {
            if (n>= 0x1000) {
                if (n>= 0x4000) {
                     xn = (table[n>> 8]) + 1;
                 } else {
                     xn = (table[n>> 6] >> 1) + 1;
                 }
             } else {
                if (n>= 0x400) {
                     xn = (table[n>> 4] >> 2) + 1;
                 } else {
                     xn = (table[n>> 2] >> 3) + 1;
                 }
             }
            return ((xn * xn) > n) ? --xn : xn;
         } else {
            if (n>= 0) {
                return table[n] >> 4;
             }
         }
     }
    return -1;
}

https://blog.csdn.net/hunterlew/article/details/45341253
https://blog.csdn.net/xtlisk/article/details/51249371
https://blog.csdn.net/wanchuanhua/article/details/5996708
http://bbs.mydigit.cn/simple/?t2329623.html
https://www.cnblogs.com/signal/p/3818332.html
https://www.cnblogs.com/atyuwen/archive/2009/11/09/1598942.html