合约
继承
Solidity 支持多重继承,并且具有多态性特性。
多态性意味着函数调用(无论是内部调用还是外部调用)始终会执行继承体系中最底层合约中具有相同名称和参数类型的函数。为了启用多态性,必须在继承体系中的每个函数上显式使用 virtual 和 override 关键字。
我们可以通过显式指定合约名来调用继承体系中更高层次的函数,例如:ContractName.functionName(),或者使用 super.functionName() 来调用继承结构中的上一级函数(参考下文“扁平化继承结构”)。
当一个合约继承其他合约时,区块链上只会创建一个合约实例,所有父合约的代码会被编译到这个实例中。这意味着对父合约函数的所有内部调用实际上是普通的内部函数调用(例如,super.f(…) 会使用 JUMP 操作码,而不是消息调用)。
状态变量遮蔽(shadowing)会被视为错误:如果某个父合约中已经声明了变量 x,子合约中不能再声明同名变量。
Solidity 的继承系统在整体上类似于 Python 的继承,尤其是在处理多重继承时,但也存在一些差异。
具体示例如下:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
contract Owned {
address payable owner;
constructor() { owner = payable(msg.sender); }
}
// 使用 `is` 关键字派生自另一个合约。
// 派生合约可以访问所有非 private 的成员,包括 internal 函数和状态变量。
// 这些成员无法通过 `this` 关键字从外部访问。
contract Emittable is Owned {
event Emitted();
// 使用 `virtual` 关键字表示该函数可以被子类重写。
function emitEvent() virtual public {
if (msg.sender == owner)
emit Emitted();
}
}
// 这些抽象合约只是为了让编译器知道接口。注意没有函数体的声明。
// 如果一个合约未实现所有函数,就只能作为接口使用。
abstract contract Config {
function lookup(uint id) public virtual returns (address adr);
}
abstract contract NameReg {
function register(bytes32 name) public virtual;
function unregister() public virtual;
}
// 支持多重继承。注意:`Owned` 同时是 `Emittable` 的父类,
// 但在最终合约中只会有一个 `Owned` 实例(类似 C++ 的虚继承)。
contract Named is Owned, Emittable {
constructor(bytes32 name) {
Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
NameReg(config.lookup(1)).register(name);
}
// 函数可以通过相同的名称和参数类型进行重写。
// 如果返回值类型不一样,将导致编译错误。
// 无论是内部调用还是消息调用,都会考虑重写关系。
// 如果我们要重写函数,必须使用 `override` 关键字。
// 如果这个函数还需要被其他合约重写,则还需要再次声明 `virtual`。
function emitEvent() public virtual override {
if (msg.sender == owner) {
Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
NameReg(config.lookup(1)).unregister();
// 仍然可以调用被重写的父类函数。
Emittable.emitEvent();
}
}
}
// 如果构造函数需要参数,则必须在派生合约的构造器中提供,
// 可以在构造函数声明中传参,也可以通过修饰器风格传参。
contract PriceFeed is Owned, Emittable, Named("GoldFeed") {
uint info;
function updateInfo(uint newInfo) public {
if (msg.sender == owner) info = newInfo;
}
// 这里只声明 `override` 而不是 `virtual`,
// 意味着继承自 PriceFeed 的合约不能再重写 emitEvent。
function emitEvent() public override(Emittable, Named) {
Named.emitEvent();
}
function get() public view returns
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
评论记录:
回复评论: