Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(13_inheritance/readme.md): fix markdown's warnings #575

Merged
merged 2 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions 13_Inheritance/DiamondInheritance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ contract God {
contract Adam is God {
function foo() public virtual override {
emit Log("Adam.foo called");
super.foo();
}

function bar() public virtual override {
Expand All @@ -35,6 +36,7 @@ contract Adam is God {
contract Eve is God {
function foo() public virtual override {
emit Log("Eve.foo called");
super.foo();
}

function bar() public virtual override {
Expand Down
75 changes: 51 additions & 24 deletions 13_Inheritance/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ tags:

# WTF Solidity极简入门: 13. 继承

我最近在重新学solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。
我最近在重新学Solidity,巩固一下细节,也写一个“WTF Solidity极简入门”,供小白们使用(编程大佬可以另找教程),每周更新1-3讲。

推特:[@0xAA_Science](https://twitter.com/0xAA_Science)

Expand All @@ -18,23 +18,28 @@ tags:
所有代码和教程开源在github: [github.com/AmazingAng/WTFSolidity](https://github.com/AmazingAng/WTFSolidity)

-----
这一讲,我们介绍`solidity`中的继承(`inheritance`),包括简单继承,多重继承,以及修饰器(`modifier`)和构造函数(`constructor`)的继承。
这一讲,我们介绍`Solidity`中的继承(`inheritance`),包括简单继承,多重继承,以及修饰器(`Modifier`)和构造函数(`Constructor`)的继承。

## 继承
继承是面向对象编程很重要的组成部分,可以显著减少重复代码。如果把合约看作是对象的话,`solidity`也是面向对象的编程,也支持继承。

继承是面向对象编程很重要的组成部分,可以显著减少重复代码。如果把合约看作是对象的话,`Solidity`也是面向对象的编程,也支持继承。

### 规则

- `virtual`: 父合约中的函数,如果希望子合约重写,需要加上`virtual`关键字。

- `override`:子合约重写了父合约中的函数,需要加上`override`关键字。

**注意**:用`override`修饰`public`变量,会重写与变量同名的`getter`函数,例如:

```solidity
mapping(address => uint256) public override balanceOf;
```

### 简单继承

我们先写一个简单的爷爷合约`Yeye`,里面包含1个`Log`事件和3个`function`: `hip()`, `pop()`, `yeye()`,输出都是”Yeye”。

```solidity
contract Yeye {
event Log(string msg);
Expand All @@ -53,7 +58,9 @@ contract Yeye {
}
}
```

我们再定义一个爸爸合约`Baba`,让他继承`Yeye`合约,语法就是`contract Baba is Yeye`,非常直观。在`Baba`合约里,我们重写一下`hip()`和`pop()`这两个函数,加上`override`关键字,并将他们的输出改为`”Baba”`;并且加一个新的函数`baba`,输出也是`”Baba”`。

```solidity
contract Baba is Yeye{
// 继承两个function: hip()和pop(),输出改为Baba。
Expand All @@ -70,10 +77,12 @@ contract Baba is Yeye{
}
}
```

我们部署合约,可以看到`Baba`合约里有4个函数,其中`hip()`和`pop()`的输出被成功改写成`”Baba”`,而继承来的`yeye()`的输出仍然是`”Yeye”`。

### 多重继承
`solidity`的合约可以继承多个合约。规则:

`Solidity`的合约可以继承多个合约。规则:

1. 继承时要按辈分最高到最低的顺序排。比如我们写一个`Erzi`合约,继承`Yeye`合约和`Baba`合约,那么就要写成`contract Erzi is Yeye, Baba`,而不能写成`contract Erzi is Baba, Yeye`,不然就会报错。

Expand All @@ -82,6 +91,7 @@ contract Baba is Yeye{
3. 重写在多个父合约中都重名的函数时,`override`关键字后面要加上所有父合约名字,例如`override(Yeye, Baba)`。

例子:

```solidity
contract Erzi is Yeye, Baba{
// 继承两个function: hip()和pop(),输出值为Erzi。
Expand All @@ -92,11 +102,15 @@ contract Erzi is Yeye, Baba{
function pop() public virtual override(Yeye, Baba) {
emit Log("Erzi");
}
}
```

我们可以看到,`Erzi`合约里面重写了`hip()`和`pop()`两个函数,将输出改为`”Erzi”`,并且还分别从`Yeye`和`Baba`合约继承了`yeye()`和`baba()`两个函数。

### 修饰器的继承

`Solidity`中的修饰器(`Modifier`)同样可以继承,用法与函数继承类似,在相应的地方加`virtual`和`override`关键字即可。

```solidity
contract Base1 {
modifier exactDividedBy2And3(uint _a) virtual {
Expand Down Expand Up @@ -124,14 +138,16 @@ contract Identifier is Base1 {
`Identifier`合约可以直接在代码中使用父合约中的`exactDividedBy2And3`修饰器,也可以利用`override`关键字重写修饰器:

```solidity
modifier exactDividedBy2And3(uint _a) override {
_;
require(_a % 2 == 0 && _a % 3 == 0);
}
modifier exactDividedBy2And3(uint _a) override {
_;
require(_a % 2 == 0 && _a % 3 == 0);
}
```

### 构造函数的继承

子合约有两种方法继承父合约的构造函数。举个简单的例子,父合约`A`里面有一个状态变量`a`,并由构造函数的参数来确定:

```solidity
// 构造函数的继承
abstract contract A {
Expand All @@ -142,29 +158,36 @@ abstract contract A {
}
}
```

1. 在继承时声明父构造函数的参数,例如:`contract B is A(1)`
2. 在子合约的构造函数中声明构造函数的参数,例如:
```solidity
contract C is A {
constructor(uint _c) A(_c * _c) {}
}
```

```solidity
contract C is A {
constructor(uint _c) A(_c * _c) {}
}
```

### 调用父合约的函数

子合约有两种方式调用父合约的函数,直接调用和利用`super`关键字。

1. 直接调用:子合约可以直接用`父合约名.函数名()`的方式来调用父合约函数,例如`Yeye.pop()`。
```solidity
1. 直接调用:子合约可以直接用`父合约名.函数名()`的方式来调用父合约函数,例如`Yeye.pop()`

```solidity
function callParent() public{
Yeye.pop();
}
```
2. `super`关键字:子合约可以利用`super.函数名()`来调用最近的父合约函数。`solidity`继承关系按声明时从右到左的顺序是:`contract Erzi is Yeye, Baba`,那么`Baba`是最近的父合约,`super.pop()`将调用`Baba.pop()`而不是`Yeye.pop()`:
```solidity
```

2. `super`关键字:子合约可以利用`super.函数名()`来调用最近的父合约函数。`Solidity`继承关系按声明时从右到左的顺序是:`contract Erzi is Yeye, Baba`,那么`Baba`是最近的父合约,`super.pop()`将调用`Baba.pop()`而不是`Yeye.pop()`:

```solidity
function callParentSuper() public{
// 将调用最近的父合约函数,Baba.pop()
super.pop();
}
```
```

### 钻石继承

Expand Down Expand Up @@ -201,6 +224,7 @@ contract God {
contract Adam is God {
function foo() public virtual override {
emit Log("Adam.foo called");
super.foo();
}

function bar() public virtual override {
Expand All @@ -212,7 +236,7 @@ contract Adam is God {
contract Eve is God {
function foo() public virtual override {
emit Log("Eve.foo called");
God.foo();
super.foo();
}

function bar() public virtual override {
Expand All @@ -235,30 +259,33 @@ contract people is Adam, Eve {

在这个例子中,调用合约`people`中的`super.bar()`会依次调用`Eve`、`Adam`,最后是`God`合约。

虽然`Eve`、`Adam`都是`God`的子合约,但整个过程中`God`合约只会被调用一次。原因是Solidity借鉴了Python的方式,强制一个由基类构成的DAG(有向无环图)使其保证一个特定的顺序。更多细节你可以查阅[Solidity的官方文档](https://solidity-cn.readthedocs.io/zh/develop/contracts.html?highlight=%E7%BB%A7%E6%89%BF#index-16)。
虽然`Eve`、`Adam`都是`God`的子合约,但整个过程中`God`合约只会被调用一次。原因是`Solidity`借鉴了Python的方式,强制一个由基类构成的DAG(有向无环图)使其保证一个特定的顺序。更多细节你可以查阅[Solidity的官方文档](https://solidity-cn.readthedocs.io/zh/develop/contracts.html?highlight=%E7%BB%A7%E6%89%BF#index-16)。

## 在Remix上验证

- 合约简单继承示例, 可以观察到Baba合约多了Yeye的函数

![13-1](./img/13-1.png)
![13-2](./img/13-2.png)
- 合约多重继承可以参考简单继承的操作步骤来增加部署Erzi合约,然后观察暴露的函数以及尝试调用来查看日志
- 修饰器继承示例

![13-3](./img/13-3.png)
![13-4](./img/13-4.png)
![13-5](./img/13-5.png)
- 构造函数继承示例

![13-6](./img/13-6.png)
![13-7](./img/13-7.png)
- 调用父合约示例

![13-8](./img/13-8.png)
![13-9](./img/13-9.png)

* 菱形继承示例
- 菱形继承示例

![13-10](./img/13-10.png)

## 总结

这一讲,我们介绍了`solidity`继承的基本用法,包括简单继承,多重继承,修饰器和构造函数的继承、调用父合约中的函数,以及多重继承中的菱形继承问题。

这一讲,我们介绍了`Solidity`继承的基本用法,包括简单继承,多重继承,修饰器和构造函数的继承、调用父合约中的函数,以及多重继承中的菱形继承问题。