第96篇 笔记-solidity中的重载(Override)

目录

1. 函数重载

2. 重载解析和参数匹配

3. 版本 0.6.x 之后的更新

4. 可变性与可见性

5. 常见问题


1. 函数重载

合约可以具有多个不同参数的同名函数,称为"重写""重载"(override ),这也适用于继承函数。

以下示例展示了合约 A 中的重载函数 f

//SPDX-License-Identifier: MIT
pragma solidity >=0.4.16 <0.9.0;

contract A {
    function f(uint _in) public pure returns (uint out) {
        out = _in;
    }

    function f(uint _in, bool _really) public pure returns (uint out) {
        if (_really)
            out = _in;
    }
}

重载函数也存在于外部接口中。如果两个外部可见函数仅区别于 Solidity 内的类型而不是它们的外部类型则会导致错误。

//SPDX-License-Identifier: MIT
pragma solidity >=0.4.16 <0.9.0;

contract A {
    function f(B _in) public pure returns (B out) {
        out = _in;
    }

    function f(address _in) public pure returns (address out) {  // 报错
        out = _in;
    }
}

contract B {}

该合约无法编译通过;报错信息:

TypeError: Function overload clash during conversion to external types for arguments.

因为:以上两个 f 函数重载都接受了 ABI 的地址类型,虽然它们在 Solidity 中被认为是不同的。

2. 重载解析和参数匹配

通过将当前范围内的函数声明与函数调用中提供的参数相匹配,可以选择重载函数。 如果所有参数都可以隐式地转换为预期类型,则选择函数作为重载候选项。如果一个候选都没有,解析失败。

注解:返回参数不作为重载解析的依据。

//SPDX-License-Identifier: MIT
pragma solidity >=0.4.16 <0.9.0;

contract A {
    function f(uint8 _in) public pure returns (uint8 out) {
        out = _in;
    }

    function f(uint256 _in) public pure returns (uint256 out) {
        out = _in;
    }
}

合约编译部署正常,但是:

  • 调用 f(50) 会导致类型错误,因为 50 既可以被隐式转换为 uint8 也可以被隐式转换为 uint256
  • 另一方面,调用 f(256) 则会解析为 f(uint256) 重载,因为 256 不能隐式转换为 uint8

3. 版本 0.6.x 之后的更新

在 solidity 的 0.6.0 版本之后:

  • 重写函数或修饰符时,必须使用新关键字 override 。
  • 接口会自动作为 virtual,在接口外部没有实现的函数必须标记为 virtual 。
  • 仅当函数被标记为 virtual 或在接口中定义时,才可以重写。
  • private 的函数是不可以标记为 virtual 的。
  • 对于单继承,请在每个重写函数中添加 override 。 
  • 对于多继承,添加 override(A, B, ..),在括号中列出所有覆盖函数的合约。

一个简单多继承的例子:

//SPDX-License-Identifier: MIT
pragma solidity ^0.6.10;
 
contract A {
    uint public x;
    function setValue(uint _x) public virtual {        //增加 virtual
        x = _x;
    }
}
 
contract B {
    uint public y;
    function setValue(uint _y) public virtual {        //增加 virtual
        y = _y;
    }
}
 
contract C is A, B {
    function setValue(uint _x) public override(A,B) {  // 重写 setValue
        A.setValue(_x);
    }
}

本文在 0.8.0 版本实测,不需要关键字 virtual 和 override 也可以:

pragma solidity >=0.4.16 <0.9.0;

contract A {
    function f(uint _in) public pure returns (uint out) {
        out = _in;
    }

    function f(uint _in, bool _really) public pure returns (uint out) {
        if (_really)
            out = _in;
    }
}

但是在 0.8.0 版本的 GitHub - OpenZeppelin/openzeppelin-contracts: OpenZeppelin Contracts is a library for secure smart contract development. 中,大量使用了 virtual 和 override。

4. 可变性与可见性

重写函数只能将覆盖函数的可见性从 external 更改为 public 。

可变性可以按照以下顺序更改为更严格的一种: nonpayable 可以被 view 和 pure 覆盖。 view 可以被 pure 覆盖。 payable 是一个例外,不能更改为任何其他可变性。

示例:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;

contract Base {
    function foo() virtual external view {}
}

contract Middle is Base {}

contract Inherited is Middle {
    function foo() override public pure {}
}

5. 常见问题

pragma solidity ^0.4.17;
 
contract Overload{
    //error,重名方法
    function fun1(){}
    function fun1(){}

    //error,返回值不能作为判断依据
    function fun2() returns(string){}
    function fun2() returns(uint){}

    //编译通过,参数类型不同可以作为重载的依据,但是传参<256时会报错,无法匹配方法
    function fun4(uint _num) {}
    function fun4(uint8 _num) {}

    //编译通过,但两者(address 与 uint160)实际存储方式一致,故无法匹配
    function fun5(uint160 account) {}
    function fun5(address account) {}
}

你可能感兴趣的:(区块链基础,以太坊,智能合约,solidity,重载)