美文网首页
10. Solidity:继承

10. Solidity:继承

作者: 泡泡龙吐泡泡 | 来源:发表于2023-10-05 19:44 被阅读0次

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合约实例有三个方法:
    1. getName():调用自己的getName()方法,返回:Keji。
    2. speak():调用Dog父合约的speak()方法,返回:Wang Wang。
    3. 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()两个函数,子合约在重写函数时都调用了父类的方法。调用父类函数方法如下:
    1. 父合约名.父合约函数。例如在D合约中:B.foo()
    2. super.父合约函数。例如在D合约中:super.bar()
  • 两种调用父合约函数的结果不一定相同。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){}
}

相关文章

网友评论

      本文标题:10. Solidity:继承

      本文链接:https://www.haomeiwen.com/subject/uzhcbdtx.html