继承
Solidity 中使用 is
关键字实现继承:
contract Father {
function getNumber() public pure returns (uint) {
return 10;
}
function getNumber2() public pure virtual returns (uint) {
return 20;
}
}
contract Son is Father {}
现在 Son 就可以调用 Father 的 getNumber、getNumber2 方法啦
函数重写
对于父合约中用 virtual 修饰的方法,可以在子合约中改用 override 修饰 并重写方法的内容:
contract Father {
function getNumber() 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 {
function getNumber() 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 Son is Father {
function getNumber2() public pure override returns (uint) {
return 40;
}
// 用 virtual 和 override 同时修饰方法
function getNumber3() public pure virtual override returns (uint) {
return 50;
}
}
contract GrandSon is Son {
function getNumber3() public pure override returns (uint) {
return 60;
}
}
多重继承
在 Solidity 中,继承顺序通过 C3 线性化算法确定。这个算法确保继承关系是一个有向无环图(DAG),并强制一个特定的继承顺序。
demo1:
X
/ |
Y |
\ |
Z
根据 C3 线性化算法,继承顺序是从最底层的合约开始,逐层向上合并继承路径。具体步骤如下:
-
从最底层合约
Z
开始:Z
继承自Y
和X
。- 继承顺序:
[Z, Y, X]
。
-
合并继承路径:
Y
继承自X
。- 继承顺序:
[Y, X]
。
-
最终继承顺序:
- 合并
Z
的继承顺序[Z, Y, X]
和Y
的继承顺序[Y, X]
。 - 确保每个合约只出现一次,并且遵循继承关系。
- 最终继承顺序为:
[Z, Y, X]
。
- 合并
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";
}
}
demo2:
X
/ \
Y A
| |
| B
\ /
Z
根据 C3 线性化算法,继承顺序是从最底层的合约开始,逐层向上合并继承路径。具体步骤如下:
-
从最底层合约
Z
开始:Z
继承自Y
和B
。- 继承顺序:
[Z, Y, B]
。
-
合并继承路径:
Y
继承自X
。B
继承自A
,A
继承自X
。- 继承顺序:
[Y, X]
和[B, A, X]
。
-
最终继承顺序:
- 合并
Z
的继承顺序[Z, Y, B]
和Y
的继承顺序[Y, X]
以及B
的继承顺序[B, A, X]
。 - 确保每个合约只出现一次,并且遵循继承关系。
- 最终继承顺序为:
[Z, Y, B, A, X]
。
- 合并
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";
}
}
contract A is X {
function foo() public virtual override returns (string memory) {
return "A";
}
}
contract B is A {
function foo() public virtual override returns (string memory) {
return "B";
}
}
contract Z is Y, B {
function foo() public pure override(Y, B) returns (string memory) {
return "Z";
}
}
调用父合约的构造函数
contract A {
string public name;
constructor(string memory _name) {
name = _name;
}
}
contract B {
uint public number;
constructor(uint _number) {
number = _number;
}
}
// demo 1 - 在构造函数后面调用基类构造函数
contract C is A, B {
constructor(string memory _name, uint _number) A(_name) B(_number) {}
}
// demo 2 - 在合约定义时调用基类构造函数
contract D is A("Hello"), B(42) {}
// demo 3 - 混合使用 demo 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
调用父合约的方法
contract A {
event Log(string message);
function func() public virtual {
emit Log("A.func");
}
}
contract B is A {
function func() public virtual override {
emit Log("B.func");
// 方法 1 - 通过 super 调用
super.func(); // A
}
}
contract C is A {
function func() public virtual override {
emit Log("C.func");
// 方法 2 - 通过合约调用
A.func(); // A
}
}
contract D is B, C {
// 方法 1 和方法 2 的区别
function func() public override(B, C) {
C.func(); // C - A
B.func(); // B - A
A.func(); // A
}
function funcD() public {
super.func(); // C - B - A
}
}