Inheritance
本节是《Solidity by Example》的中文翻译与深入讲解,专为零基础或刚接触区块链开发的小白朋友打造。我们将通过“示例 + 解说 + 提示”的方式,带你逐步理解每一段 Solidity 代码的实际用途与背后的逻辑。
Solidity 是以太坊等智能合约平台使用的主要编程语言,就像写网页要用 HTML 和 JavaScript,写智能合约就需要会 Solidity。
如果你从没写过区块链代码也没关系,只要你了解一点点编程概念,比如“变量”“函数”“条件判断”,我们就能从最简单的例子开始,一步步建立你的 Solidity 编程思维。
Inheritance
继承
Solidity 支持多重继承。合约可以通过 is
关键字继承其他合约。
将被子合约覆盖的函数必须声明为 virtual
。
覆盖父合约函数的函数必须使用 override
关键字。
继承顺序很重要。
必须按照从“最基础”到“最衍生”的顺序列出父合约。
什么是 Solidity 继承?
在 Solidity 中,继承允许一个合约(子合约)获取另一个合约(父合约)的功能(如函数、状态变量),通过 is
关键字实现。
- 比喻:继承像连锁餐厅的分店(子合约)继承总店(父合约)的品牌、菜单和运营规则,同时可以自定义部分内容(如菜单项)。
- 支持多重继承,一个子合约可继承多个父合约(如
D is B, C
)。
关键字的作用:
-
virtual
:标记父合约的函数,允许子合约覆盖(重写)其逻辑。- 比喻:像总店规定“菜单可以调整”,允许分店自定义。
-
override
:子合约中标记覆盖父合约的函数,确保明确修改。- 比喻:分店声明“我改了总店的菜单”。
-
继承顺序:从“最基础”(如核心功能)到“最衍生”(如扩展功能),影响构造函数调用(参考历史对话中的构造函数)和函数搜索顺序。
- 比喻:像连锁店先继承总部的核心规则(
A
),再添加区域特色(B
,C
)。
- 比喻:像连锁店先继承总部的核心规则(
-
多重继承的搜索规则:
- 当子合约调用父合约函数(如
super.foo()
)时,Solidity 按从右到左、深度优先的顺序搜索父合约。 - 比喻:像分店需要一道菜的配方,先查最近的区域总部(右边父合约),再查总店。
- 当子合约调用父合约函数(如
// SPDX-License-Identifier: MIT
// 使用 MIT 许可证,允许自由使用、修改和分发代码。pragma solidity ^0.8.26;
// 指定 Solidity 编译器版本,必须为 0.8.26 或更高(但低于 0.9.0)。/* Graph of inheritanceA/ \B C/ \ /
F D,E
*/
// 继承关系图:
// - A 是基础合约(父合约)。
// - B 和 C 继承 A。
// - D 和 E 继承 B 和 C(多重继承)。
// - F 继承 A 和 B。
// 表示合约之间的层级关系,D 和 E 是多重继承的复杂案例。contract A {function foo() public pure virtual returns (string memory) {return "A";}// 定义基础合约 A。// 函数 foo:// - public:外部和内部可调用。// - pure:不读写区块链状态,仅返回字符串 "A"。// - virtual:允许子合约覆盖。// - returns (string memory):返回内存中的字符串。// 比喻:A 是总店,提供默认菜单项 "A"。
}// Contracts inherit other contracts by using the keyword 'is'.
// 合约通过 'is' 关键字继承其他合约。
contract B is A {// Override A.foo()// 覆盖 A 的 foo 函数function foo() public pure virtual override returns (string memory) {return "B";}// 定义合约 B,继承 A(通过 is A)。// 函数 foo:// - 覆盖 A 的 foo 函数(使用 override 关键字)。// - virtual:允许进一步的子合约(如 D、F)覆盖。// - 返回 "B"。// 比喻:B 是分店,改用自己的菜单项 "B"。
}contract C is A {// Override A.foo()// 覆盖 A 的 foo 函数function foo() public pure virtual override returns (string memory) {return "C";}// 定义合约 C,继承 A。// 函数 foo:// - 覆盖 A 的 foo 函数,返回 "C"。// - virtual:允许进一步覆盖。// 比喻:C 是另一家分店,菜单项改为 "C"。
}// Contracts can inherit from multiple parent contracts.
// When a function is called that is defined multiple times in
// different contracts, parent contracts are searched from
// right to left, and in a depth-first manner.
// 合约可以继承多个父合约。
// 当调用一个在多个父合约中定义的函数时,父合约按从右到左、深度优先的顺序搜索。contract D is B, C {// D.foo() returns "C"// since C is the right most parent contract with function foo()// D.foo() 返回 "C",因为 C 是最右边的父合约,包含 foo 函数。function foo() public pure override(B, C) returns (string memory) {return super.foo();}// 定义合约 D,继承 B 和 C(多重继承)。// 函数 foo:// - override(B, C):覆盖 B 和 C 的 foo 函数(需明确指定)。// - super.foo():调用父合约的 foo 函数。// - 搜索顺序:从右到左(C → B),C 是最右父合约,调用 C 的 foo,返回 "C"。// 比喻:D 是综合分店,查菜单时先看 C 的("C"),因为 C 在继承列表中更靠右。
}contract E is C, B {// E.foo() returns "B"// since B is the right most parent contract with function foo()// E.foo() 返回 "B",因为 B 是最右边的父合约,包含 foo 函数。function foo() public pure override(C, B) returns (string memory) {return super.foo();}// 定义合约 E,继承 C 和 B(顺序相反)。// 函数 foo:// - override(C, B):覆盖 C 和 B 的 foo 函数。// - super.foo():调用父合约的 foo 函数。// - 搜索顺序:从右到左(B → C),B 是最右父合约,调用 B 的 foo,返回 "B"。// 比喻:E 是另一家综合分店,查菜单时先看 B 的("B"),因为 B 在继承列表中更靠右。
}// Inheritance must be ordered from "most base-like" to "most derived".
// Swapping the order of A and B will throw a compilation error.
// 继承必须从“最基础”到“最衍生”排序。
// 交换 A 和 B 的顺序将导致编译错误。
contract F is A, B {function foo() public pure override(A, B) returns (string memory) {return super.foo();}// 定义合约 F,继承 A 和 B。// 继承顺序:A(基础)→ B(衍生,继承 A)。// 函数 foo:// - override(A, B):覆盖 A 和 B 的 foo 函数。// - super.foo():调用父合约的 foo 函数。// - 搜索顺序:从右到左(B → A),B 是最右父合约,调用 B 的 foo,返回 "B"。// 如果写成 is B, A(违反“基础到衍生”),编译报错,因为 B 依赖 A。// 比喻:F 是分店,先继承总店 A 的核心规则,再继承 B 的特色菜单。
}
代码包含六个合约,展示继承机制、多重继承、函数覆盖和搜索顺序:
- 合约
A
:基础合约,定义可被覆盖的foo
函数(返回"A"
)。 - 合约
B
和C
:继承A
,分别覆盖foo
返回"B"
和"C"
。 - 合约
D
和E
:多重继承B
和C
,通过super.foo()
展示父合约函数的搜索顺序(右到左)。 - 合约
F
:继承A
和B
,验证继承顺序规则(从基础到衍生)。
继承的本质
- 继承允许子合约复用父合约的代码(函数、状态变量),并通过覆盖(
override
)自定义逻辑。 - 比喻:
- 父合约像连锁餐厅的总店,提供标准菜单(
foo
返回"A"
)。 - 子合约像分店,继承总店菜单(
is A
),可自定义(B
返回"B"
,C
返回"C"
)。 - 多重继承(
D is B, C
)像分店结合多家总部的特色,调用时按顺序选择(右到左)。
- 父合约像连锁餐厅的总店,提供标准菜单(
- 核心功能:
- 代码复用:子合约继承父合约的函数和状态变量,减少重复代码。
- 函数覆盖:通过
virtual
和override
修改父合约逻辑。 - 多重继承:支持继承多个父合约,函数调用按右到左、深度优先搜索。
- 继承顺序:从“最基础”(如
A
)到“最衍生”(如B
),确保依赖关系正确。
代码功能
- 合约
A
:- 基础合约,定义
foo
函数(返回"A"
),标记为virtual
允许覆盖。 - 模拟总店的核心功能。
- 基础合约,定义
- 合约
B
和C
:- 继承
A
,覆盖foo
分别返回"B"
和"C"
,仍标记virtual
允许进一步覆盖。 - 模拟分店的定制功能。
- 继承
- 合约
D
和E
:- 多重继承
B
和C
,通过super.foo()
调用父合约的foo
。 D is B, C
:调用C.foo()
(返回"C"
,因C
更靠右)。E is C, B
:调用B.foo()
(返回"B"
,因B
更靠右)。- 展示多重继承的搜索规则(右到左)。
- 多重继承
- 合约
F
:- 继承
A
和B
,验证继承顺序(A
→B
)和super.foo()
(返回"B"
)。 - 强调“基础到衍生”的顺序规则。
- 继承
继承的注意事项
- 继承顺序:
- 必须从“最基础”到“最衍生”(如
A, B
,B
依赖A
)。 - 错误顺序(如
B, A
)导致编译错误,因依赖关系不满足。
- 必须从“最基础”到“最衍生”(如
- 函数覆盖:
- 父合约函数需标记
virtual
,子合约覆盖需用override
。 - 多重继承需明确指定覆盖的父合约(如
override(B, C)
)。 super
调用按右到左、深度优先搜索,确保逻辑清晰。
- 父合约函数需标记
- Gas 成本:
- 多重继承增加部署 Gas(因需初始化多个父合约)。
super
调用增加 Gas(因搜索父合约),尽量简化