Loading docs/graph/bridge.md +45 −1 Original line number Diff line number Diff line Loading @@ -115,7 +115,13 @@ low[u] = min(low[u], num[v]); 和割点差不多,还叫做割桥。 > 对于一个无向图,如果删掉一条边后图中的连通分量数增加了,则称这条边为桥或者割边。 > 对于一个无向图,如果删掉一条边后图中的连通分量数增加了,则称这条边为桥或者割边。严谨来说,就是:假设有连通图 $G=\{V,E\}$ , $e$ 是其中一条边(即 $e \in E$ ),如果 $G-e$ 是不连通的,则边 $e$ 是图 $G$ 的一条割边(桥)。 比如说,下图中,  红色箭头指向的就是割边。 ### 实现 Loading @@ -123,4 +129,42 @@ low[u] = min(low[u], num[v]); 割边是和是不是根节点没关系的,原来我们求割点的时候是指点 $v$ 是不可能不经过父节点 $u$ 为回到祖先节点(包括父节点),所以顶点 $u$ 是割点。如果 $low_v=num_u$ 表示还可以回到父节点,如果顶点 $v$ 不能回到祖先也没有另外一条回到父亲的路,那么 $u-v$ 这条边就是割边。 ### 代码实现 下面代码实现了求割边,其中,当 `isbridge[x]` 为真时, `(father[x],x)` 为一条割边。 ```cpp int low[MAXN], dfn[MAXN], iscut[MAXN], dfs_clock; bool isbridge[MAXN]; vector<int> G[MAXN]; int cnt_bridge; int father[MAXN]; void tarjan(int u, int fa) { father[u] = fa; low[u] = dfn[u] = ++dfs_clock; for (int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if (!dfn[v]) { tarjan(v, u); low[u] = min(low[u], low[v]); if (low[v] > dfn[u]) { isbridge[v] = true; ++cnt_bridge; } } else if (dfn[v] < dfn[u] && v != fa) { low[u] = min(low[u], dfn[v]); } } } ``` ## 练习 - [P3388【模板】割点(割顶)](https://www.luogu.org/problem/P3388) - [POJ2117 Electricity](https://vjudge.net/problem/POJ-2117) - [HDU4738 Caocao's Bridges](https://vjudge.net/problem/HDU-4738) - [HDU2460 Network](https://vjudge.net/problem/HDU-2460) - [POJ1523 SPF](https://vjudge.net/problem/POJ-1523) Tarjan 算法还有许多用途,常用的例如求强连通分量,缩点,还有求 2-SAT 的用途等。 docs/graph/images/bridge4.png 0 → 100644 +15.6 KiB Loading image diff... docs/lang/class.md +330 −0 Original line number Diff line number Diff line author: Ir1d, cjsoft, Lans1ot 类(class)是结构体的拓展,不仅能够拥有成员元素,还拥有成员函数。 在面向对象编程(OOP)中,对象就是类的实例,也就是变量。 C++ 中 `struct` 关键字定义的也是类,上文中的 **结构体** 的定义来自 C。因为某些历史原因,C++ 保留并拓展了 `struct` 。 ## 定义类 类使用关键字 `class` 或者 `struct` 定义,下文以 `class` 举例。 ```cpp class ClassName { ... }; // Example: class Object { public: int weight; int value; } e[array_length]; const Object a; Object b, B[array_length]; Object *c; ``` 与使用 `struct` 大同小异。该例定义了一个名为 `Object` 的类。该类拥有四个成员元素,分别为 `weight,value` ;并在 `}` 后定义了一个数组 `B` 。 定义类的指针形同 [ `struct` ](./struct.md) 。 ### 访问说明符 不同于 [ `struct` ](./struct.md) 中的举例,本例中出现了 `public` ,这属于访问说明符。 - `public` :该访问说明符之后的各个成员都可以被公开访问,简单来说就是无论 **类内** 还是 **类外** 都可以访问。 - `protected` :该访问说明符之后的各个成员可以被 **类内** 、派生类或者友元的成员访问,但类外 **不能访问** 。 - `private` :该访问说明符之后的各个成员 **只能** 被 **类内** 成员或者友元的成员访问, **不能** 被从类外或者派生类中访问。 对于 `struct` ,它的所有成员都是默认 `public` 。对于 `class` ,它的所有成员都是默认 `private` 。 _关于 "友元" 和 "派生类",可以参考下方折叠框,或者查询网络资料进行详细了解。_ _对于算法竞赛来说,友元和派生类并不是必须要掌握的知识点。_ ??? note "关于友元以及派生类的基本概念" 友元(`friend`): 使用`friend`关键字修饰某个函数或者类。可以使得在**被修饰者**在不成为成员函数或者成员类的情况下,访问该类的私有(`private`)或者受保护(`protected`)成员。简单来说就是只要带有这个类的`friend`标记,就可以访问私有或受保护的成员元素。 派生类(`derived class`): C++允许使用一个类作为**基类**,并通过基类**派生**出**派生类**。其中派生类(根据特定规则)继承基类中的成员变量和成员函数。可以提高代码的复用率。 派生类似" is "的关系。如猫(派生类)" is " 哺乳动物(基类)。 对于上面`private`和`protected`的区别,可以看做派生类可以访问基类的`protected`的元素(`public`同),但不能访问`private`元素。 ## 访问与修改成员元素的值 方法形同 [ `struct` ](./struct.md) - 对于变量,使用 `.` 符号。 - 对于指针,使用 `->` 符号。 ## 成员函数 成员函数,顾名思义。就是类中所包含的函数。 ??? note "常见成员函数举例" ```cpp vector.push_back(); set.insert(); queue.empty(); ``` ```cpp class Class_Name { ... type Funciton_Name(...) { ... } }; // Example: class Object { public: int weight; int value; void print() { cout << weight << endl; return; } void change_w(int); }; void Object::change_w(int _weight) { weight = _weight; } ``` 该类有一个打印 `Object` 成员元素的函数,以及更改成员元素 `weight` 的函数。 和函数类似,对于成员函数,也可以先声明,在定义,如第十四行(声明处)以及十七行后(定义处)。 如果想要调用 `var` 的 `print` 成员函数,可以使用 `var.print()` 进行调用。 ### 重载运算符 ??? note "何为重载" C++ 允许编写者为名称相同的函数或者运算符指定不同的定义。这称为 **重载** (overload)。 如果同名函数的参数种类、数量、返回类型不相同其中一者或多者两两不相同,则这些同名函数被看做是不同的。 如果在调用时不会出现混淆(指调用某些同名函数时,无法根据所填参数种类和数量唯一地判断出被调用函数。常发生在具有默认参数的函数中),则编译器会根据调用时所填参数判断应调用函数。 而上述过程被称作重载解析。 重载运算符,可以部分程度上代替函数,简化代码。 下面给出重载运算符的例子。 ```cpp class Vector { public: int x, y; Vector() : x(0), y(0) {} Vector(int _x, int _y) : x(_x), y(_y) {} int operator*(const Vector& other) { return x * other.y + y * other.x; } Vector operator+(const Vector&); Vector operator-(const Vector&); }; Vector Vector::operator+(const Vector& other) { return Vector(x + other.x, y + other.y); } Vector Vector::operator-(const Vector& other) { return Vector(x - other.x, y - other.y); } //关于4,5行表示为x,y赋值,具体实现参见后文。 ``` 该例定义了一个向量类,并重载了 `* + -` 运算符,并分别代表向量内积,向量加,向量减。 重载运算符的模板大致可分为下面几部分。 ```text /*类定义内重载*/ 返回类型 operator符号(参数){...} /*类定义内声明,在外部定义*/ 返回类型 类名称::operator符号(参数){...} ``` 对于自定义的类,如果重载了某些运算符(一般来说只需要重载 `<` 这个比较运算符),便可以使用相应的 STL 容器或算法,如 [ `sort` ](../basic/stl-sort.md) 。 _如要了解更多,参见“参考资料”第四条。_ ??? note " 可以被重载的运算符" ```cpp = + - * / = % += -= *= /= %= < > == != <= >= & | ! ^ ~ &= |= ^= //---------- << <<= >> >>= ++ -- && || [] () , ->* -> new delete new[] delete[] ``` ### 在实例化变量时设定初始值 为完成这种操作,需要定义 **默认构造函数** (Default constructor)。 ```cpp class ClassName { ... ClassName(...)... { ... } }; // Example: class Object { public: int weight; int value; Object() { weight = 0; value = 0; } }; ``` 该例定义了 `Object` 的默认构造函数,该函数能够在我们实例化 `Object` 类型变量时,将所有的成员元素初始化为 `0` 。 若无显式的构造函数,则编译器认为该类有隐式的默认构造函数。换言之,若无定义任何构造函数,则编译器会自动生成一个默认构造函数,并会根据成员元素的类型进行初始化(与定义 内置类型 变量相同)。 在这种情况下,成员元素都是未初始化的,访问未初始化的变量的结果是未定义的(也就是说并不知道会返回和值)。 如果需要自定义初始化的值,可以再定义(或重载)构造函数。 ??? note "关于定义(或重载)构造函数" 一般来说,默认构造函数是不带参数的,这区别于构造函数。构造函数和默认构造函数的定义大同小异,只是参数数量上的不同。 构造函数可以被重载(当然首次被叫做定义)。需要注意的是,如果已经定义了构造函数,且构造函数的参数列表不为空,那么编译器便不会再生成无参数的默认构造函数。这会可能会使试图以默认方法构造变量的行为编译失败(指不填入初始化参数)。 使用 C++11 或以上时,可以使用 `{}` 进行变量的初始化。 ??? note "关于`{}`" 使用 `{}` 进行初始化,会用到 std::initializer_list 这一个轻量代理对象进行初始化。 初始化步骤大概如下 1. 尝试寻找参数中有`std::initializer_list`的默认构造函数,如果有则调用(调用完后不再进行下面的查找,下同)。 2. 尝试将`{}`中的元素填入其他构造参数,如果能将参数按照顺序填满(默认参数也算在内),则调用该默认构造函数。 3. 若无`private`成员元素,则尝试在**类外**按照元素定义顺序或者下标顺序依次赋值。 _上述过程只是完整过程的简化版本,详细内容参见"参考资料九"_ ```cpp class Object { public: int weight; int value; Object() { weight = 0; value = 0; } Object(int _weight = 0, int _value = 0) { weight = _weight; value = _value; } // the same as // Object(int _weight,int _value):weight(_weight),value(_value) {} }; // the same as // Object::Object(int _weight,int _value){ // weight = _weight; // value = _value; // } //} Object A; // ok Object B(1, 2); // ok Object C{1, 2}; // ok,(C++11) ``` ??? note "关于隐式类型转换" 有时候会写出如下的代码 ```cpp class Node { public: int var; Node(int _var) : var(_var) {} }; Node a = 1; ``` 看上去十分不符合逻辑,一个 `int` 类型不可能转化为 `node` 类型。但是编译器不会进行 `error` 提示。 原因是在进行赋值时,首先会将 `1` 作为参数调用 `node::node(int)` ,然后调用默认的复制函数进行赋值。 但大多数情况下,编写者会希望编译器进行报错。这时便可以在构造函数前追加 `explicit` 关键字。这会告诉编译器必须显式进行调用。 ```cpp class Node { public: int var; explicit Node(int _var) : var(_var) {} }; ``` 也就是说 `node a=1` 将会报错,但 `node a=node(1)` 不会。因为后者显式调用了构造函数。当然大多数人不会写出后者的代码,但此例足以说明 explicit 的作用。 _不过在算法竞赛中,为了避免此类情况常用的是"加强对代码的规范程度",从源头上避免_ ### 销毁 这是不可避免的问题。每一个变量都将在作用范围结束走向销毁。 但对于已经指向了动态申请的内存的指针来说,该指针在销毁时不会自动释放所指向的内存,需要手动释放动态内存。 如果结构体的成员元素包含指针,同样会遇到这种问题。需要用到析构函数来手动释放动态内存。 **析构** 函数(Destructor)将会在该变量被销毁时被调用。重载的方法形同构造函数,但需要在前加 `~` _默认定义的析构函数通常对于算法竞赛已经足够使用,通常我们只有在成员元素包含指针时才会重载析构函数。_ ```cpp class Object { public: int weight; int value; int* ned; Object() { weight = 0; value = 0; } ~Object() { delete ned; } }; ``` ### 为类变量赋值 默认情况下,赋值时会按照对应成员元素赋值的规则进行。也可以使用 `类名称()` 或 `类名称{}` 作为临时变量来进行赋值。 前者只是调用了复制构造函数(copy constructor),而后者在调用复制构造函数前会调用默认构造函数。 另外默认情况下,进行的赋值都是对应元素间进行 **浅拷贝** ,如果成员元素中有指针,则在赋值完成后,两个变量的成员指针具有相同的地址。 ```cpp // A,tmp1,tmp2,tmp3类型为Object tmp1 = A; tmp2 = Object(...); tmp3 = {...}; ``` 如需解决指针问题或更多操作,需要重载相应的构造函数。 _更多 构造函数(constructor)内容,参见“参考资料”第六条。_ ## 参考资料 1. [cppreference class](https://zh.cppreference.com/w/cpp/language/class) 2. [cppreference access](https://zh.cppreference.com/w/cpp/language/access) 3. [cppreference default_constructor](https://zh.cppreference.com/w/cpp/language/default_constructor) 4. [cppreference operator](https://zh.cppreference.com/w/cpp/language/operators) 5. [cplusplus Data structures](http://www.cplusplus.com/doc/tutorial/structures/) 6. [cplusplus Special members](http://www.cplusplus.com/doc/tutorial/classes2/) 7. [C++11 FAQ](http://www.stroustrup.com/C++11FAQ.html) 8. [cppreference Friendship and inheritance](http://www.cplusplus.com/doc/tutorial/inheritance/) 9. [cppreference value initialization](https://zh.cppreference.com/w/cpp/language/value_initialization) docs/lang/java.md +36 −26 Original line number Diff line number Diff line Loading @@ -22,22 +22,11 @@ Java 是一种广泛使用的计算机编程语言,拥有 **跨平台** 、 **  ### macOS/Linux ### Linux ```bash sudo mv jdk-14 /opt ``` #### 使用包管理器安装 然后打开 `.bashrc` 文件在文件末尾添加一些命令 ```bash export JAVA_HOME="/opt/jdk-14/bin" export PATH=${JAVA_HOME}:$PATH ``` 然后在控制台当中输入命令 `source ~/.bashrc` , 如果是使用的 zsh, 那么同样的在 `~/.zshrc` 当中添加上面的内容 以上是手动安装的方式,嫌麻烦可以使用在线安装命令如下 可以使用包管理器提供的 JDK。具体指令如下 ```bash sudo apt install default-jre Loading @@ -58,6 +47,23 @@ sudo yum localinstall jre-9.0.4_linux_x64_bin.rpm #安装jre-9.0 sudo yum localinstall jdk-9.0.4_linux-x64_bin.rpm #安装jdk-9.0 ``` #### 手动安装 ```bash sudo mv jdk-14 /opt ``` 并在 `.bashrc` 文件末尾添加 ```bash export JAVA_HOME="/opt/jdk-14/bin" export PATH=${JAVA_HOME}:$PATH ``` 在控制台中输入命令 `source ~/.bashrc` 即可重载。如果是使用的 zsh 或其他命令行,在 `~/.zshrc` 或对应的文件中添加上面的内容 ### MacOS 如果是 MacOS,你可以使用以下命令安装包 ```bash Loading @@ -69,7 +75,7 @@ diskutil umount /Volumes/JDK\ 8\ Update\ 121 rm jdk-8u121-macosx-x64.dmg ``` 或者直接在官方网站下载 `pkg` 包或是 `dmg` 包安装 或者直接在官方网站下载 `pkg` 包或 `dmg` 包安装 ## 基本语法 Loading Loading @@ -104,7 +110,7 @@ double PI = 3.1415926; ### final 关键字 `final` 含义是这是最终的、不可更改的结果,被 final 修饰的变量只能被赋值一次,赋值后不再改变 `final` 含义是这是最终的、不可更改的结果,被 final 修饰的变量只能被赋值一次,赋值后不再改变。 ```java final double PI = 3.1415926; Loading Loading @@ -259,18 +265,22 @@ class test{ } ``` #### 注意事项 ## 注意事项 - 1\. 创建 Java 源程序需要类名和文件名一致才能编译通过,否则编译器会提示找不到 `类` , 例子: ### 类名与文件名一致 创建 Java 源程序需要类名和文件名一致才能编译通过,否则编译器会提示找不到 `类` 。通常该文件名会在具体 OJ 中指定。 例: Add.java ```java class Add{ Add(int x,int y){ return x+y; public static void main(String[] args) { // ... } } ``` 上面的类中的方法和类名一致,这被称为构造函数,所以在调用此类的时候可以直接使用 `int number = new Add(12,12)` ; 在该文件中需使用 Add 为类名方可编译通过。 docs/lang/struct.md +70 −0 Original line number Diff line number Diff line author: Ir1d, cjsoft, Lans1ot 结构体(struct),可以看做是一系列称为成员元素的组合体。 可以看做是自定义的数据类型。 _本页描述的 `struct` 不同于 C 中 `struct` ,在 C++ 中 `struct` 被扩展为类似 [ `class` ](./class.md) 的类说明符_。 ## 定义结构体 ```cpp struct Object { int weight; int value; } e[array_length]; const Object a; Object b, B[array_length], tmp; Object *c; ``` 上例中定义了一个名为 `Object` 的结构体,两个成员元素 `value,weight` ,类型都为 `int` 。 在 `}` 后,定义了数据类型为 `Object` 的常量 `a` ,变量 `b` ,变量 `tmp` ,数组 `B` ,指针 `c` 。对于某种已经存在的类型,都可以使用这里的方法进行定义常量、变量、指针、数组等。 _关于指针:不必强求掌握。_ ### 定义指针 如果是定义内置类型的指针,则与平常定义指针一样。 如果是定义结构体指针,在定义中使用 `StructName*` 进行定义。 ```cpp struct Edge { /* ... */ Edge* nxt; }; ``` 上例仅作举例,不必纠结实际意义。 ## 访问/修改成员元素 可以使用 `变量名.成员元素名` 进行访问(其中双引号不写入程序,下同)。 如 : 输出 `var` 的 `v` 成员: `cout << var.v` 。 也可以使用 `指针名->成员元素名` 或者 使用 `(*指针名).成员元素名` 进行访问。 如 : 将结构体指针 `ptr` 指向的结构体的成员元素 `v` 赋值为 `tmp` : `(*ptr).v = tmp` 或者 `ptr->v = tmp` 。 ## 为什么需要结构体? 首先,条条大路通罗马,可以不使用结构体达到相同的效果。但是结构体能够显式地将成员元素(在算法竞赛中通常是变量)捆绑在一起,如本例中的 `Object` 结构体,便将 `value,weight` 放在了一起(定义这个结构体的实际意义是表示一件物品的重量与价值)。这样的好处边是限制了成员元素的使用。 想象一下,如果不使用结构体而且有两个数组 `value[],Value[]` ,很容易写混淆。但如果使用结构体,能够减轻出现使用变量错误的几率。 并且不同的结构体(结构体类型,如 `Object` 这个结构体)或者不同的结构体变量(结构体的实例,如上方的 `e` 数组)可以拥有相同名字的成员元素(如 `tmp.value,b.value` ),同名的成员元素相互独立(拥有独自的内存,比如说修改 `tmp.value` 不会影响 `b.value` 的值)。 这样的好处是可以使用尽可能相同或者相近的变量去描述一个物品。比如说 `Object` 里有 `value` 这个成员变量;我们还可以定义一个 `Car` 结构体,同时也拥有 `value` 这个成员;如果不使用结构体,或许我们就需要定义 `valueOfObject[],valueOfCar[]` 等不同名称的数组来区分。 _如果想要更详细的描述一种事物,还可以定义成员函数。请参考 [类](./class.md) 获取详细内容。_ ## 更多的操作? 详见 [类](./class.md) ## 参考资料 1. [cppreference class](https://zh.cppreference.com/w/cpp/language/class) 2. [cplusplus Data structures](http://www.cplusplus.com/doc/tutorial/structures/) Loading
docs/graph/bridge.md +45 −1 Original line number Diff line number Diff line Loading @@ -115,7 +115,13 @@ low[u] = min(low[u], num[v]); 和割点差不多,还叫做割桥。 > 对于一个无向图,如果删掉一条边后图中的连通分量数增加了,则称这条边为桥或者割边。 > 对于一个无向图,如果删掉一条边后图中的连通分量数增加了,则称这条边为桥或者割边。严谨来说,就是:假设有连通图 $G=\{V,E\}$ , $e$ 是其中一条边(即 $e \in E$ ),如果 $G-e$ 是不连通的,则边 $e$ 是图 $G$ 的一条割边(桥)。 比如说,下图中,  红色箭头指向的就是割边。 ### 实现 Loading @@ -123,4 +129,42 @@ low[u] = min(low[u], num[v]); 割边是和是不是根节点没关系的,原来我们求割点的时候是指点 $v$ 是不可能不经过父节点 $u$ 为回到祖先节点(包括父节点),所以顶点 $u$ 是割点。如果 $low_v=num_u$ 表示还可以回到父节点,如果顶点 $v$ 不能回到祖先也没有另外一条回到父亲的路,那么 $u-v$ 这条边就是割边。 ### 代码实现 下面代码实现了求割边,其中,当 `isbridge[x]` 为真时, `(father[x],x)` 为一条割边。 ```cpp int low[MAXN], dfn[MAXN], iscut[MAXN], dfs_clock; bool isbridge[MAXN]; vector<int> G[MAXN]; int cnt_bridge; int father[MAXN]; void tarjan(int u, int fa) { father[u] = fa; low[u] = dfn[u] = ++dfs_clock; for (int i = 0; i < G[u].size(); i++) { int v = G[u][i]; if (!dfn[v]) { tarjan(v, u); low[u] = min(low[u], low[v]); if (low[v] > dfn[u]) { isbridge[v] = true; ++cnt_bridge; } } else if (dfn[v] < dfn[u] && v != fa) { low[u] = min(low[u], dfn[v]); } } } ``` ## 练习 - [P3388【模板】割点(割顶)](https://www.luogu.org/problem/P3388) - [POJ2117 Electricity](https://vjudge.net/problem/POJ-2117) - [HDU4738 Caocao's Bridges](https://vjudge.net/problem/HDU-4738) - [HDU2460 Network](https://vjudge.net/problem/HDU-2460) - [POJ1523 SPF](https://vjudge.net/problem/POJ-1523) Tarjan 算法还有许多用途,常用的例如求强连通分量,缩点,还有求 2-SAT 的用途等。
docs/lang/class.md +330 −0 Original line number Diff line number Diff line author: Ir1d, cjsoft, Lans1ot 类(class)是结构体的拓展,不仅能够拥有成员元素,还拥有成员函数。 在面向对象编程(OOP)中,对象就是类的实例,也就是变量。 C++ 中 `struct` 关键字定义的也是类,上文中的 **结构体** 的定义来自 C。因为某些历史原因,C++ 保留并拓展了 `struct` 。 ## 定义类 类使用关键字 `class` 或者 `struct` 定义,下文以 `class` 举例。 ```cpp class ClassName { ... }; // Example: class Object { public: int weight; int value; } e[array_length]; const Object a; Object b, B[array_length]; Object *c; ``` 与使用 `struct` 大同小异。该例定义了一个名为 `Object` 的类。该类拥有四个成员元素,分别为 `weight,value` ;并在 `}` 后定义了一个数组 `B` 。 定义类的指针形同 [ `struct` ](./struct.md) 。 ### 访问说明符 不同于 [ `struct` ](./struct.md) 中的举例,本例中出现了 `public` ,这属于访问说明符。 - `public` :该访问说明符之后的各个成员都可以被公开访问,简单来说就是无论 **类内** 还是 **类外** 都可以访问。 - `protected` :该访问说明符之后的各个成员可以被 **类内** 、派生类或者友元的成员访问,但类外 **不能访问** 。 - `private` :该访问说明符之后的各个成员 **只能** 被 **类内** 成员或者友元的成员访问, **不能** 被从类外或者派生类中访问。 对于 `struct` ,它的所有成员都是默认 `public` 。对于 `class` ,它的所有成员都是默认 `private` 。 _关于 "友元" 和 "派生类",可以参考下方折叠框,或者查询网络资料进行详细了解。_ _对于算法竞赛来说,友元和派生类并不是必须要掌握的知识点。_ ??? note "关于友元以及派生类的基本概念" 友元(`friend`): 使用`friend`关键字修饰某个函数或者类。可以使得在**被修饰者**在不成为成员函数或者成员类的情况下,访问该类的私有(`private`)或者受保护(`protected`)成员。简单来说就是只要带有这个类的`friend`标记,就可以访问私有或受保护的成员元素。 派生类(`derived class`): C++允许使用一个类作为**基类**,并通过基类**派生**出**派生类**。其中派生类(根据特定规则)继承基类中的成员变量和成员函数。可以提高代码的复用率。 派生类似" is "的关系。如猫(派生类)" is " 哺乳动物(基类)。 对于上面`private`和`protected`的区别,可以看做派生类可以访问基类的`protected`的元素(`public`同),但不能访问`private`元素。 ## 访问与修改成员元素的值 方法形同 [ `struct` ](./struct.md) - 对于变量,使用 `.` 符号。 - 对于指针,使用 `->` 符号。 ## 成员函数 成员函数,顾名思义。就是类中所包含的函数。 ??? note "常见成员函数举例" ```cpp vector.push_back(); set.insert(); queue.empty(); ``` ```cpp class Class_Name { ... type Funciton_Name(...) { ... } }; // Example: class Object { public: int weight; int value; void print() { cout << weight << endl; return; } void change_w(int); }; void Object::change_w(int _weight) { weight = _weight; } ``` 该类有一个打印 `Object` 成员元素的函数,以及更改成员元素 `weight` 的函数。 和函数类似,对于成员函数,也可以先声明,在定义,如第十四行(声明处)以及十七行后(定义处)。 如果想要调用 `var` 的 `print` 成员函数,可以使用 `var.print()` 进行调用。 ### 重载运算符 ??? note "何为重载" C++ 允许编写者为名称相同的函数或者运算符指定不同的定义。这称为 **重载** (overload)。 如果同名函数的参数种类、数量、返回类型不相同其中一者或多者两两不相同,则这些同名函数被看做是不同的。 如果在调用时不会出现混淆(指调用某些同名函数时,无法根据所填参数种类和数量唯一地判断出被调用函数。常发生在具有默认参数的函数中),则编译器会根据调用时所填参数判断应调用函数。 而上述过程被称作重载解析。 重载运算符,可以部分程度上代替函数,简化代码。 下面给出重载运算符的例子。 ```cpp class Vector { public: int x, y; Vector() : x(0), y(0) {} Vector(int _x, int _y) : x(_x), y(_y) {} int operator*(const Vector& other) { return x * other.y + y * other.x; } Vector operator+(const Vector&); Vector operator-(const Vector&); }; Vector Vector::operator+(const Vector& other) { return Vector(x + other.x, y + other.y); } Vector Vector::operator-(const Vector& other) { return Vector(x - other.x, y - other.y); } //关于4,5行表示为x,y赋值,具体实现参见后文。 ``` 该例定义了一个向量类,并重载了 `* + -` 运算符,并分别代表向量内积,向量加,向量减。 重载运算符的模板大致可分为下面几部分。 ```text /*类定义内重载*/ 返回类型 operator符号(参数){...} /*类定义内声明,在外部定义*/ 返回类型 类名称::operator符号(参数){...} ``` 对于自定义的类,如果重载了某些运算符(一般来说只需要重载 `<` 这个比较运算符),便可以使用相应的 STL 容器或算法,如 [ `sort` ](../basic/stl-sort.md) 。 _如要了解更多,参见“参考资料”第四条。_ ??? note " 可以被重载的运算符" ```cpp = + - * / = % += -= *= /= %= < > == != <= >= & | ! ^ ~ &= |= ^= //---------- << <<= >> >>= ++ -- && || [] () , ->* -> new delete new[] delete[] ``` ### 在实例化变量时设定初始值 为完成这种操作,需要定义 **默认构造函数** (Default constructor)。 ```cpp class ClassName { ... ClassName(...)... { ... } }; // Example: class Object { public: int weight; int value; Object() { weight = 0; value = 0; } }; ``` 该例定义了 `Object` 的默认构造函数,该函数能够在我们实例化 `Object` 类型变量时,将所有的成员元素初始化为 `0` 。 若无显式的构造函数,则编译器认为该类有隐式的默认构造函数。换言之,若无定义任何构造函数,则编译器会自动生成一个默认构造函数,并会根据成员元素的类型进行初始化(与定义 内置类型 变量相同)。 在这种情况下,成员元素都是未初始化的,访问未初始化的变量的结果是未定义的(也就是说并不知道会返回和值)。 如果需要自定义初始化的值,可以再定义(或重载)构造函数。 ??? note "关于定义(或重载)构造函数" 一般来说,默认构造函数是不带参数的,这区别于构造函数。构造函数和默认构造函数的定义大同小异,只是参数数量上的不同。 构造函数可以被重载(当然首次被叫做定义)。需要注意的是,如果已经定义了构造函数,且构造函数的参数列表不为空,那么编译器便不会再生成无参数的默认构造函数。这会可能会使试图以默认方法构造变量的行为编译失败(指不填入初始化参数)。 使用 C++11 或以上时,可以使用 `{}` 进行变量的初始化。 ??? note "关于`{}`" 使用 `{}` 进行初始化,会用到 std::initializer_list 这一个轻量代理对象进行初始化。 初始化步骤大概如下 1. 尝试寻找参数中有`std::initializer_list`的默认构造函数,如果有则调用(调用完后不再进行下面的查找,下同)。 2. 尝试将`{}`中的元素填入其他构造参数,如果能将参数按照顺序填满(默认参数也算在内),则调用该默认构造函数。 3. 若无`private`成员元素,则尝试在**类外**按照元素定义顺序或者下标顺序依次赋值。 _上述过程只是完整过程的简化版本,详细内容参见"参考资料九"_ ```cpp class Object { public: int weight; int value; Object() { weight = 0; value = 0; } Object(int _weight = 0, int _value = 0) { weight = _weight; value = _value; } // the same as // Object(int _weight,int _value):weight(_weight),value(_value) {} }; // the same as // Object::Object(int _weight,int _value){ // weight = _weight; // value = _value; // } //} Object A; // ok Object B(1, 2); // ok Object C{1, 2}; // ok,(C++11) ``` ??? note "关于隐式类型转换" 有时候会写出如下的代码 ```cpp class Node { public: int var; Node(int _var) : var(_var) {} }; Node a = 1; ``` 看上去十分不符合逻辑,一个 `int` 类型不可能转化为 `node` 类型。但是编译器不会进行 `error` 提示。 原因是在进行赋值时,首先会将 `1` 作为参数调用 `node::node(int)` ,然后调用默认的复制函数进行赋值。 但大多数情况下,编写者会希望编译器进行报错。这时便可以在构造函数前追加 `explicit` 关键字。这会告诉编译器必须显式进行调用。 ```cpp class Node { public: int var; explicit Node(int _var) : var(_var) {} }; ``` 也就是说 `node a=1` 将会报错,但 `node a=node(1)` 不会。因为后者显式调用了构造函数。当然大多数人不会写出后者的代码,但此例足以说明 explicit 的作用。 _不过在算法竞赛中,为了避免此类情况常用的是"加强对代码的规范程度",从源头上避免_ ### 销毁 这是不可避免的问题。每一个变量都将在作用范围结束走向销毁。 但对于已经指向了动态申请的内存的指针来说,该指针在销毁时不会自动释放所指向的内存,需要手动释放动态内存。 如果结构体的成员元素包含指针,同样会遇到这种问题。需要用到析构函数来手动释放动态内存。 **析构** 函数(Destructor)将会在该变量被销毁时被调用。重载的方法形同构造函数,但需要在前加 `~` _默认定义的析构函数通常对于算法竞赛已经足够使用,通常我们只有在成员元素包含指针时才会重载析构函数。_ ```cpp class Object { public: int weight; int value; int* ned; Object() { weight = 0; value = 0; } ~Object() { delete ned; } }; ``` ### 为类变量赋值 默认情况下,赋值时会按照对应成员元素赋值的规则进行。也可以使用 `类名称()` 或 `类名称{}` 作为临时变量来进行赋值。 前者只是调用了复制构造函数(copy constructor),而后者在调用复制构造函数前会调用默认构造函数。 另外默认情况下,进行的赋值都是对应元素间进行 **浅拷贝** ,如果成员元素中有指针,则在赋值完成后,两个变量的成员指针具有相同的地址。 ```cpp // A,tmp1,tmp2,tmp3类型为Object tmp1 = A; tmp2 = Object(...); tmp3 = {...}; ``` 如需解决指针问题或更多操作,需要重载相应的构造函数。 _更多 构造函数(constructor)内容,参见“参考资料”第六条。_ ## 参考资料 1. [cppreference class](https://zh.cppreference.com/w/cpp/language/class) 2. [cppreference access](https://zh.cppreference.com/w/cpp/language/access) 3. [cppreference default_constructor](https://zh.cppreference.com/w/cpp/language/default_constructor) 4. [cppreference operator](https://zh.cppreference.com/w/cpp/language/operators) 5. [cplusplus Data structures](http://www.cplusplus.com/doc/tutorial/structures/) 6. [cplusplus Special members](http://www.cplusplus.com/doc/tutorial/classes2/) 7. [C++11 FAQ](http://www.stroustrup.com/C++11FAQ.html) 8. [cppreference Friendship and inheritance](http://www.cplusplus.com/doc/tutorial/inheritance/) 9. [cppreference value initialization](https://zh.cppreference.com/w/cpp/language/value_initialization)
docs/lang/java.md +36 −26 Original line number Diff line number Diff line Loading @@ -22,22 +22,11 @@ Java 是一种广泛使用的计算机编程语言,拥有 **跨平台** 、 **  ### macOS/Linux ### Linux ```bash sudo mv jdk-14 /opt ``` #### 使用包管理器安装 然后打开 `.bashrc` 文件在文件末尾添加一些命令 ```bash export JAVA_HOME="/opt/jdk-14/bin" export PATH=${JAVA_HOME}:$PATH ``` 然后在控制台当中输入命令 `source ~/.bashrc` , 如果是使用的 zsh, 那么同样的在 `~/.zshrc` 当中添加上面的内容 以上是手动安装的方式,嫌麻烦可以使用在线安装命令如下 可以使用包管理器提供的 JDK。具体指令如下 ```bash sudo apt install default-jre Loading @@ -58,6 +47,23 @@ sudo yum localinstall jre-9.0.4_linux_x64_bin.rpm #安装jre-9.0 sudo yum localinstall jdk-9.0.4_linux-x64_bin.rpm #安装jdk-9.0 ``` #### 手动安装 ```bash sudo mv jdk-14 /opt ``` 并在 `.bashrc` 文件末尾添加 ```bash export JAVA_HOME="/opt/jdk-14/bin" export PATH=${JAVA_HOME}:$PATH ``` 在控制台中输入命令 `source ~/.bashrc` 即可重载。如果是使用的 zsh 或其他命令行,在 `~/.zshrc` 或对应的文件中添加上面的内容 ### MacOS 如果是 MacOS,你可以使用以下命令安装包 ```bash Loading @@ -69,7 +75,7 @@ diskutil umount /Volumes/JDK\ 8\ Update\ 121 rm jdk-8u121-macosx-x64.dmg ``` 或者直接在官方网站下载 `pkg` 包或是 `dmg` 包安装 或者直接在官方网站下载 `pkg` 包或 `dmg` 包安装 ## 基本语法 Loading Loading @@ -104,7 +110,7 @@ double PI = 3.1415926; ### final 关键字 `final` 含义是这是最终的、不可更改的结果,被 final 修饰的变量只能被赋值一次,赋值后不再改变 `final` 含义是这是最终的、不可更改的结果,被 final 修饰的变量只能被赋值一次,赋值后不再改变。 ```java final double PI = 3.1415926; Loading Loading @@ -259,18 +265,22 @@ class test{ } ``` #### 注意事项 ## 注意事项 - 1\. 创建 Java 源程序需要类名和文件名一致才能编译通过,否则编译器会提示找不到 `类` , 例子: ### 类名与文件名一致 创建 Java 源程序需要类名和文件名一致才能编译通过,否则编译器会提示找不到 `类` 。通常该文件名会在具体 OJ 中指定。 例: Add.java ```java class Add{ Add(int x,int y){ return x+y; public static void main(String[] args) { // ... } } ``` 上面的类中的方法和类名一致,这被称为构造函数,所以在调用此类的时候可以直接使用 `int number = new Add(12,12)` ; 在该文件中需使用 Add 为类名方可编译通过。
docs/lang/struct.md +70 −0 Original line number Diff line number Diff line author: Ir1d, cjsoft, Lans1ot 结构体(struct),可以看做是一系列称为成员元素的组合体。 可以看做是自定义的数据类型。 _本页描述的 `struct` 不同于 C 中 `struct` ,在 C++ 中 `struct` 被扩展为类似 [ `class` ](./class.md) 的类说明符_。 ## 定义结构体 ```cpp struct Object { int weight; int value; } e[array_length]; const Object a; Object b, B[array_length], tmp; Object *c; ``` 上例中定义了一个名为 `Object` 的结构体,两个成员元素 `value,weight` ,类型都为 `int` 。 在 `}` 后,定义了数据类型为 `Object` 的常量 `a` ,变量 `b` ,变量 `tmp` ,数组 `B` ,指针 `c` 。对于某种已经存在的类型,都可以使用这里的方法进行定义常量、变量、指针、数组等。 _关于指针:不必强求掌握。_ ### 定义指针 如果是定义内置类型的指针,则与平常定义指针一样。 如果是定义结构体指针,在定义中使用 `StructName*` 进行定义。 ```cpp struct Edge { /* ... */ Edge* nxt; }; ``` 上例仅作举例,不必纠结实际意义。 ## 访问/修改成员元素 可以使用 `变量名.成员元素名` 进行访问(其中双引号不写入程序,下同)。 如 : 输出 `var` 的 `v` 成员: `cout << var.v` 。 也可以使用 `指针名->成员元素名` 或者 使用 `(*指针名).成员元素名` 进行访问。 如 : 将结构体指针 `ptr` 指向的结构体的成员元素 `v` 赋值为 `tmp` : `(*ptr).v = tmp` 或者 `ptr->v = tmp` 。 ## 为什么需要结构体? 首先,条条大路通罗马,可以不使用结构体达到相同的效果。但是结构体能够显式地将成员元素(在算法竞赛中通常是变量)捆绑在一起,如本例中的 `Object` 结构体,便将 `value,weight` 放在了一起(定义这个结构体的实际意义是表示一件物品的重量与价值)。这样的好处边是限制了成员元素的使用。 想象一下,如果不使用结构体而且有两个数组 `value[],Value[]` ,很容易写混淆。但如果使用结构体,能够减轻出现使用变量错误的几率。 并且不同的结构体(结构体类型,如 `Object` 这个结构体)或者不同的结构体变量(结构体的实例,如上方的 `e` 数组)可以拥有相同名字的成员元素(如 `tmp.value,b.value` ),同名的成员元素相互独立(拥有独自的内存,比如说修改 `tmp.value` 不会影响 `b.value` 的值)。 这样的好处是可以使用尽可能相同或者相近的变量去描述一个物品。比如说 `Object` 里有 `value` 这个成员变量;我们还可以定义一个 `Car` 结构体,同时也拥有 `value` 这个成员;如果不使用结构体,或许我们就需要定义 `valueOfObject[],valueOfCar[]` 等不同名称的数组来区分。 _如果想要更详细的描述一种事物,还可以定义成员函数。请参考 [类](./class.md) 获取详细内容。_ ## 更多的操作? 详见 [类](./class.md) ## 参考资料 1. [cppreference class](https://zh.cppreference.com/w/cpp/language/class) 2. [cplusplus Data structures](http://www.cplusplus.com/doc/tutorial/structures/)