继承是面向对象编程很重要的组成部分,可以显著减少重复代码。如果把合约看作是对象的话,Solidity也是面向对象的编程,也支持继承。
- virtual: 父合约中的函数,如果希望子合约重写,需要加上virtual关键字。
- override:子合约重写了父合约中的函数,需要加上override关键字。
注意:用override修饰public变量,会重写与变量同名的getter函数.
mapping(address => uint256) public override balanceOf;
先写一个简单的爷爷合约Yeye,里面包含1个Log事件和3个function: hip(), pop(), yeye(),输出都是”Yeye”。
contract Yeye {
event Log(string msg);
// 定义3个function: hip(), pop(), man(),Log值为Yeye。
function hip() public virtual{
emit Log("Yeye");
}
function pop() public virtual{
emit Log("Yeye");
}
function yeye() public virtual {
emit Log("Yeye");
}
}
再定义一个爸爸合约Baba,让他继承Yeye合约
contract Baba is Yeye{
// 继承两个function: hip()和pop(),输出改为Baba。
function hip() public virtual override{
emit Log("Baba");
}
function pop() public virtual override{
emit Log("Baba");
}
function baba() public virtual{
emit Log("Baba");
}
}
部署合约,可以看到Baba合约里有4个函数,其中hip()和pop()的输出被成功改写成”Baba”,而继承来的yeye()的输出仍然是”Yeye”
Solidity的合约可以继承多个合约。规则如下:
-
继承时要按辈分最高到最低的顺序排。
-
如果某一个函数在多个继承的合约里都存在,在子合约里必须重写,不然会报错。
-
重写在多个父合约中都重名的函数时,override关键字后面要加上所有父合约名字.
Solidity中的修饰器(Modifier)同样可以继承,用法与函数继承类似,在相应的地方加virtual和override关键字即可。
子合约有两种方法继承父合约的构造函数。
- 在继承时声明父构造函数的参数
contract B is A(1)
- 在子合约的构造函数中声明构造函数的参数
contract C is A {
constructor(uint _c) A(_c * _c) {}
}
子合约有两种方式调用父合约的函数,直接调用和利用super关键字。
- 直接调用:子合约可以直接用父合约名.函数名()的方式来调用父合约函数.
function callParent() public{
Yeye.pop();
}
- super关键字:子合约可以利用super.函数名()来调用最近的父合约函数。
在面向对象编程中,钻石继承(菱形继承)指一个派生类同时有两个或两个以上的基类。
在多重+菱形继承链条上使用super关键字时,需要注意的是使用super会调用继承链条上的每一个合约的相关函数,而不是只调用最近的父合约。