继承
Solidity 中使用 is
关键字实现继承,子合约可以继承父合约的状态变量、函数、事件、装饰器:
contract Father {
// 声明事件
event Log(string message, address sender);
// 声明状态变量
uint public a = 10;
address public owner = msg.sender;
// 声明装饰器
modifier onlyOwner() {
require(msg.sender == owner, "You are not the owner");
_;
}
// 声明函数
function setA(uint _a) public {
a = _a;
}
}
contract Son is Father {
function testEvent() public onlyOwner {
emit Log("Son", msg.sender);
}
}
-
部署 Son 合约
-
可以看到 Son 合约继承了 Father 合约的状态变量和函数
-
调用 Son 合约的 testEvent 方法,可以看到触发了 Father 合约的事件
-
用其他账户调用 Son 合约的 testEvent 方法,会报错,可知 Son 合约继承了 Father 合约的修饰器
重写函数
-
使用
virtual
关键字标记父合约中的函数,表示该函数可以在子合约中被重写 -
使用
override
关键字标记子合约中的函数,表示该函数重写了父合约中的函数
contract Father {
function getNumber1() public pure returns (uint) {
return 10;
}
function getNumber2() public pure virtual returns (uint) {
return 20;
}
}
contract Son is Father {
function getNumber2() public pure override returns (uint) {
return 40;
}
}
重写装饰器
规则与重写函数相同。
contract Father {
modifier limit(uint256 _value) virtual {
require(_value > 0 && _value < 100, "value must be between 0 and 100");
_;
}
}
contract Son is Father {
modifier limit(uint256 _value) override {
require(_value > 0 && _value < 10, "value must be between 0 and 10");
_;
}
}
多级继承
contract A {
function getNumber1() public pure returns (uint) {
return 10;
}
function getNumber2() public pure virtual returns (uint) {
return 20;
}
function getNumber3() public pure virtual returns (uint) {
return 30;
}
}
contract B is A {
function getNumber2() public pure override returns (uint) {
return 40;
}
// 用 virtual 和 override 同时修饰方法
function getNumber3() public pure virtual override returns (uint) {
return 50;
}
}
contract C is B {
function getNumber3() public pure override returns (uint) {
return 60;
}
}
多重继承
多重继承允许一个合约继承多个父合约。
规则:
-
继承顺序:继承时要按辈分从高到低排序。
-
函数重写:如果多个父合约中存在同名函数,子合约必须重写该函数,并在 override 关键字后面列出所有父合约的名字。
X
/ |
Y |
\ |
Z
contract X {
function foo() public virtual returns (string memory) {
return "X";
}
}
contract Y is X {
function foo() public virtual override returns (string memory) {
return "Y";
}
}
// 根据继承顺序, 先 X 后 Y
contract Z is X, Y {
// override 里面的顺序无所谓
function foo() public pure override(Y, X) returns (string memory) {
return "Z";
}
}
钻石继承 (菱形继承)
菱形继承 (钻石继承) 是多重继承的一种特殊情况。
A
/ \
B C
\ /
D
contract A {
function foo() public pure virtual returns (string memory) {
return "A";
}
}
contract B is A {
function foo() public pure virtual override returns (string memory) {
return "B";
}
}
contract C is A {
function foo() public pure virtual override returns (string memory) {
return "C";
}
}
// 根据继承顺序, B C 平级, 所以顺序随意
contract D1 is B, C {
function foo() public pure override(B, C) returns (string memory) {
return "D1";
}
}
contract D2 is C, B {
function foo() public pure override(C, B) returns (string memory) {
return "D2";
}
}
调用父合约的构造函数
contract A {
string public name;
constructor(string memory _name) {
name = _name;
}
}
contract B {
uint public number;
constructor(uint _number) {
number = _number;
}
}
// 方法 1 - 在声明继承时调用
contract D is A("Hello"), B(42) {}
// 方法 2 - 在声明构造函数时调用
contract C is A, B {
constructor(string memory _name, uint _number) A(_name) B(_number) {}
}
// 方法 1 和 2 可混用
contract E is A("Hello"), B {
constructor(uint _number) B(_number) {}
}
构造函数的调用顺序:先继承的父合约 → 后继承的父合约 → 当前合约
所以,demo 1 是 A → B → C、 demo 2 是 A → B → D、 demo 3 是 A → B → E
调用父合约的方法
对于多重继承:
X
/ |
Y |
\ |
Z
contract A {
event Log(string message);
function func() public virtual {
emit Log("A");
}
}
contract B is A {
function func() public virtual override {
emit Log("B");
super.func();
}
}
contract C is A, B {
function func() public virtual override(A, B) {
// 方法 1 - 通过 super 调用, 会调用最近的父合约
super.func(); // B - A
// 方法 2 - 通过合约调用
A.func(); // A
B.func(); // B - A
}
}
对于钻石继承:
A
/ \
B C
\ /
D
contract A {
event Log(string message);
function func() public virtual {
emit Log("A");
}
}
contract B is A {
function func() public virtual override {
emit Log("B");
super.func();
}
}
contract C is A {
function func() public virtual override {
emit Log("C");
super.func();
}
}
// 根据继承顺序, B C 平级, 所以顺序随意
// 先继承 C 再继承 B
contract D1 is C, B {
function func() public override(B, C) {
C.func(); // C - A
B.func(); // B - C - A
A.func(); // A
}
function funcD() public {
super.func(); // B - C - A
}
}
// 先继承 B 再继承 C
contract D2 is B, C {
function func() public override(B, C) {
C.func(); // C - B - A
B.func(); // B - A
A.func(); // A
}
function funcD() public {
super.func(); // C - B - A
}
}