开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情
函数调用方式
- 内部调用
- 又称“消息调用” 常见的有:合约内部函数、父合约的函数以及库函数的调用 例子:假设A合约中存在f函数,则在A合约内部,其他函数调用f函数的调用方式为f()
- 外部调用
- 又称“EVM调用” 一般为跨合约的函数调用。在同一合约内部,也可以产生外部调用 假设A合约中存在f函数,则在B合约内使用A.f()调用为外部调用。在A合约内部,用this.f()来调用也是外部调用
- 函数标识符
-
函数的可见性由高到低: public > external > internal > private
-
没有标识符,默认为public
-
-
状态变量的可见性
-
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函数
- 一个合约最多能有一个fallback函数,声明为: