Solidity入门学习(3)——函数

157 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情

函数调用方式

  • 内部调用
    • 又称“消息调用” 常见的有:合约内部函数、父合约的函数以及库函数的调用 例子:假设A合约中存在f函数,则在A合约内部,其他函数调用f函数的调用方式为f()
  • 外部调用
    • 又称“EVM调用” 一般为跨合约的函数调用。在同一合约内部,也可以产生外部调用 假设A合约中存在f函数,则在B合约内使用A.f()调用为外部调用。在A合约内部,用this.f()来调用也是外部调用
  • 函数标识符
    • 函数的可见性由高到低: public > external > internal > private

    • 没有标识符,默认为public

    • 函数标识符.png

状态变量的可见性

  • public

    • 对于 public 状态变量会自动生成一个getter函数,而不会生成setter函数,这样其他合约可以读取此状态变量的值,但是不能修改其值
  • internal

    • 这是状态变量的默认可见性
    • 只能在定义它的合约和派生合约里访问internal变量
  • private

    • 仅定义此状态变量的合约中可见,派生合约不可见

函数的状态可变性

按照状态可变性,可以将函数分为2类:

  • view:保证不修改状态。比如getter函数。
  • pure:承诺不读取也不修改状态。
  • 修改状态的行为:参考solidity教程

函数修饰器

结合具体的例子来看函数修饰器Modifier的作用

pragma solidity >=0.7.1 <0.9.0;

contract owned {
    constructor() { owner = payable(msg.sender); }

    address owner;

    // 这个合约只定义一个修改器,但并未使用: 它将会在派生合约中用到。
    // 修改器所修饰的函数体会被插入到特殊符号 _; 的位置。
    // 这意味着如果是 owner 调用这个函数,则函数会被执行,否则会抛出异常。
    modifier onlyOwner {
        require(
            msg.sender == owner,
            "Only owner can call this function."
        );
        _;
    }
}

contract destructible is owned {
    // 这个合约从 `owned` 继承了 `onlyOwner` 修饰符,并将其应用于 `destroy` 函数,
    // 只有在合约里保存的 owner 调用 `destroy` 函数,才会生效。
    function destroy() public onlyOwner {
        selfdestruct(owner);
    }
}

从上面的例子中可以看到:

  • 占位符“_”下划线的作用是把函数体放置在此处
  • 某函数使用了modifier,那么在执行函数之前,会把函数体放到modifier的占位符处,顺序执行modifier中的语句,如果在modifier中出现多出占位符,则每一处都做函数体带入。
  • modifier是合约的可继承属性,也可能在派生方法里被覆盖(如果被标记为virtual)

另一个例子:

contract Mutex {
    bool locked;
    modifier noReentrancy() {
        require(
            !locked,
            "Reentrant call."
        );
        locked = true;
        _;
        locked = false;
    }

    // 这个函数受互斥量保护,这意味着 `msg.sender.call` 中的重入调用不能再次调用  `f`。
    // `return 7` 语句指定返回值为 7,但修改器中的语句 `locked = false` 仍会执行。
    function f() public noReentrancy returns (uint) {
        (bool success,) = msg.sender.call("");
        require(success);
        return 7;
    }
}

此例中,执行顺序是,先把f()带入noReentrancy的占位符处,开始顺序执行noReentrancy中的语句,进入f()中执行在执行完return语句后,return跳出的是f()函数,noReentrancy占位符后的”locked=false;“语句仍会被执行。

特殊函数

可以接收以太的函数:receive函数和payable标记的fallback函数。推荐用receive函数来接收。

  • receive函数

    • 一个合约最多能有一个 receive函数, 声明为: receive() external payable { ... }
    • 没有 function 关键字、入参、返回值
    • 必须是external可见性和payable 修饰
    • 可以是 virtual 的,可以被重载也可以有modifier
  • fallback函数

    • 一个合约最多能有一个fallback函数,声明为:fallback () external [payable]或 fallback (bytes calldata input) external [payable] returns (bytes memory output)
    • 没有 function 关键字、入参、返回值
    • 必须是external可见性
    • 可以是 virtual 的,可以被重载也可以有modifier
    • 如果fallback函数为了接受以太,那必须定义为payable类型
    • 合约调用时,如果没有找到对应的方法,就会自动调用fallback函数