10.1 单继承
继承是面向对象编程很重要的组成部分,可以显著减少重复代码。如果把合约看作是对象的话,solidity也是面向对象的编程,也支持继承。
10.1.1 virtual和override
- virtual:在父合约中,如果允许自合约重写该函数,则函数应当加上virtual关键字。
 - override:在子合约中,如果要对父合约中的函数进行重写,则应加上override关键字。
 
10.1.2 例子
contract Animal {
    function getName() public pure virtual returns (string memory) {
        return "Animal";
    } 
    function walk() public pure virtual returns (string memory) {
        return "walk";
    }
}
contract Dog is Animal {
    function getName() public pure override returns (string memory) {
        return "Dog";
    }
}
- 上述代码定义了一个父合约:Animal,一个子合约:Dog。Dog合约继承Animal合约,语法为:
contract Dog is Animal {}。 - 父合约Animal中定义了两个函数:
getName()和walk(),并且都用virtual关键字进行修饰,即允许子合约重写。 - 子合约Dog中,重写了父合约的
getName()方法。(使用关键字override修饰)。 - 部署子合约,调用
getName()方法,返回值为“Dog”,即该方法被重写成功了。 - 对于方法
walk(),子合约没有进行重写,因此调用的是父合约的walk()方法,返回值依然为“walk”。 
10.2 多线继承
solidity中支持多继承,即一个合约可以继承自多个合约。我们再定义一个Keji合约,继承自Animal和Dog两个合约。
继承结构如下:
//    Animal
//    /    \
//   Dog    \
//    \     /
//     \   /
//     Keji
示例代码如下:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract Animal {
    function getName() public pure virtual returns (string memory) {
        return "Animal";
    } 
    function walk() public pure virtual returns (string memory) {
        return "walk";
    }
}
contract Dog is Animal {
    function getName() public pure override virtual returns (string memory) {
        return "Dog";
    }
    function speak() public pure virtual returns (string memory) {
        return "Wang Wang";
    }
}
contract Keji is Animal, Dog {
    function getName() public pure override(Dog, Animal) returns (string memory) {
        return "Keji";
    }
}
注意:
- 多继语句为:
contract ContractC is ContractA, ContractB{},其中ContractA是比ContractB更上层的合约,顺序不能错,否则编译不通过。本例中,Animal比Dog更上层,因此Keji合约的定义语句为:contract Keji is Animal, Dog{},Animal在前,Dog在后。 - 如果一个函数,在多个父合约中均有实现,则子合约必需重回写,否则编译不通过。例如,
getName()函数必需在Keji合约重重写。 - 重写关键字
override后要加上重写函数所在的函数名,顺序不做要求:override(Dog, Animal)。 - 本例中,Keji合约实例有三个方法:
- 
getName():调用自己的getName()方法,返回:Keji。 - 
speak():调用Dog父合约的speak()方法,返回:Wang Wang。 - 
walk():调用Animal父合约的walk()方法,返回:walk。 
 - 
 
10.3 菱形继承(钻石继承)
我们构造一种继承关系,B和C合约继承A合约,而D合约由继承B和C合约,继承关系如下:
//      A
//    /   \
//   B     C
//    \   /
//      D
这种继承机构称为菱形继承或者钻石继承。示例代码如下:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
contract A {
    event Log(string message);
    function foo() public virtual{
        emit Log("A.foo");
    }
    function bar() public virtual{
        emit Log("A.bar");
    }
}
contract B is A {
    function foo() public override  virtual {
        A.foo();
        emit Log("B.foo");
    }
    function bar() public override  virtual {
        super.bar();
        emit Log("B.bar");
    }
}
contract C is A {
    function foo() public override  virtual {
        A.foo();
        emit Log("C.foo");
    }
    function bar() public override  virtual {
        super.bar();
        emit Log("C.bar");
    }
}
contract D is B,C {
    function foo() public override(B,C)  virtual {
        B.foo();
        emit Log("D.foo");
    }
    function bar() public override(B,C)  virtual {
        super.bar();
        emit Log("D.bar");
    }
}
注意:
- 每个合约中都有
foo()和bar()两个函数,子合约在重写函数时都调用了父类的方法。调用父类函数方法如下:- 父合约名.父合约函数。例如在D合约中:
B.foo()。 - super.父合约函数。例如在D合约中:
super.bar()。 
 - 父合约名.父合约函数。例如在D合约中:
 - 两种调用父合约函数的结果不一定相同。
B.foo()只会调用父合约B的对应函数,而super.bar()会调用所有父合约(B,C)对应的函数。 - D合约中调用了B、C合约的
bar()方法,B、C合约中的bar()方法又都调用了A合约中的bar()方法,但是A合约中的bar()方法只会被调用一次。原因是Solidity借鉴了Python的方式,强制一个由基类构成的DAG(有向无环图)使其保证一个特定的顺序。更多细节你可以查阅Solidity的官方文档。 - 因此,本例中D合约
foo()和bar()输出的事件消息分别如下: 
// *****************************  foo()  *****************************
[
    {
        "from": "0xad153c5e12dB58a47f2454691b26582A430803fB",
        "topic": "0xcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab",
        "event": "Log",
        "args": {
            "0": "A.foo",
            "message": "A.foo"
        }
    },
    {
        "from": "0xad153c5e12dB58a47f2454691b26582A430803fB",
        "topic": "0xcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab",
        "event": "Log",
        "args": {
            "0": "B.foo",
            "message": "B.foo"
        }
    },
    {
        "from": "0xad153c5e12dB58a47f2454691b26582A430803fB",
        "topic": "0xcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab",
        "event": "Log",
        "args": {
            "0": "D.foo",
            "message": "D.foo"
        }
    }
]
// *****************************  bar()  *****************************
[
    {
        "from": "0xad153c5e12dB58a47f2454691b26582A430803fB",
        "topic": "0xcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab",
        "event": "Log",
        "args": {
            "0": "A.bar",
            "message": "A.bar"
        }
    },
    {
        "from": "0xad153c5e12dB58a47f2454691b26582A430803fB",
        "topic": "0xcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab",
        "event": "Log",
        "args": {
            "0": "B.bar",
            "message": "B.bar"
        }
    },
    {
        "from": "0xad153c5e12dB58a47f2454691b26582A430803fB",
        "topic": "0xcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab",
        "event": "Log",
        "args": {
            "0": "C.bar",
            "message": "C.bar"
        }
    },
    {
        "from": "0xad153c5e12dB58a47f2454691b26582A430803fB",
        "topic": "0xcf34ef537ac33ee1ac626ca1587a0a7e8e51561e5514f8cb36afa1c5102b3bab",
        "event": "Log",
        "args": {
            "0": "D.bar",
            "message": "D.bar"
        }
    }
]
10.4 修饰器继承
Solidity中的修饰器(Modifier)同样可以继承,用法与函数继承类似,在相应的地方加virtual和override关键字即可。
contract A {
    modifier requireGreater(uint _a) virtual  {
        require(_a > 5, "Require greater than 5");
        _;
    }
}
contract B is A {
    function double(uint _a) public pure requireGreater(_a) returns (uint){
        return _a * 2;
    }
}
重写修饰器:
contract C is A {
    modifier requireGreater(uint _a) override  {
        require(_a > 10, "Require greater than 10");
        _;
    }
    function double(uint _a) public pure requireGreater(_a) returns (uint){
        return _a * 2;
    }
}
10.5 构造函数继承
子合约有两种方法继承父合约的构造函数。举个简单的例子,父合约A里面有一个状态变量a,并由构造函数的参数来确定:
contract A {
    uint public a;
    constructor(uint _a) {
        a = _a;
    }
}
方法一:在继承时传入父构造函数的参数
contract B is A(5) {}
方法二:在子合约的构造函数中声明构造函数的参数
contract C is A {
    constructor(uint _a) A(_a*2){}
}