2018-10-26笔记
合约相当于一个类,包含有使用持久性存储的状态变量,用来修改状态变量的函数。调用其他合约的函数,会切换当前上下文环境到新合约中。
创建合约
在创建合的时候,会执行合约的constructor
,其中constructor
可有可无,如果有只能有一个。当节点执行完构造函数后,状态变量跟函数会部署到节点中,构造函数与只在构造函数中使用的内部函数不会被部署到节点中。
成员作用域
Solidity只有两种函数调用方式,内部调用和外部调用(即通过消息调用
的方式)。但是,合约的状态变量与方法共有4种可见范围。
- external只可以通过外部调用的方式使用,如果合约内部需要使用一个
external
成员,需要使用this
,这个主要用来与其他外部合约对接使用。 - public:这个标识的可以通过内部调用或者外部调用的方式来访问成员。对状态变量使用
public
会为其生成一个getter
。 - internal:这个标识的只能内部访问,且派生合约可以访问,相当于
protected
。 - private:这个就是
private
了,同样不可以被派生类访问。
Getter方法
声明为public
的状态变量,编译器会为其制动生成一个同名getter
方法,当没有使用this
(这个表示合约实例对象)的是否,是通过internal
的方式进行访问,反之则是使用external的方式。例如下面这个例子:
pragma solidity >=0.4.0 <0.6.0;
contract C {
uint public data;
function f() public view returns(uint) {
data = 10; //内部访问方式
return data;
//return this.data;
//编译器报错,因为使用this相当于外部访问
//需要用函数调用的形式访问变量
//即 return this.data(); 的方式代替
}
}
contract Caller {
C c = new C();
function f() public view returns (uint) {
//在Caller中调用C长度data状态变量,需要使用.data()
//如果去掉(),则编译器会报错,无法将c.data转换成uint类型返回
return c.data();
}
}
Function Modifiers
这个有点像是python的装饰器,可以给函数增加一些功能,看一下官方的例子:
contract owned {
constructor() public { owner = msg.sender; }
address payable owner;
modifier onlyOwner {
require(
msg.sender == owner,
);
_; //这个表示被装饰的函数调用位置
}
}
contract mortal is owned {
function close() public onlyOwner { //这里表示使用了onlyOwner这个Modifier
selfdestruct(owner);
}
}
上面的例子就相当于
contract mortal {
function close() public {
require(
msg.sender == owner,
);
selfdestruct(owner);
}
}
如果一个函数要使用多个Modifier,则使用空格分隔各个Modifier,并且Modifiers会按序执行。举个例子,
pragma solidity >0.4.99 <0.6.0;
contract A1 {
uint a;
modifier Func1 {
require(a > 0, "\n**********\nneed a > 0\n**********\n");
_;
}
modifier Func2 {
if(a == 0) {
a = 10;
}
_;
}
function f1() public view returns(uint) {
return a;
}
function f2() public Func1 Func2 returns(uint) {
return a;
}
function f3() public Func2 Func1 returns(uint) {
a = 1;
return a;
}
}
首先查看f1()
的的结果,在不使用Modifier的情况下,会直接返回a
的值0
。
再看f2()
的结果,发现出现了Error
,原因是require
的时候,a
的值还是0
最后看看f3()
的结果,
综合上面三个输出结果,可分析得出结论,执行顺序与列举Modifier的顺序是一致的,首先执行
modifier Func2
将a的值变成了
10
,然后再
modifier Func1
中满足了
require
要求,最后才执行
f3()
的函数体,这时候
a
被赋值为
1
。
函数类型
合约函数总共有两种类型,可以理解成用来表示function
对调用者的承诺。
view
:表示该函数不会读取状态变量,但承诺不会去修改状态变量,相当于是一个只读函数。
pure
:表示该函数不会访问状态变量,就只使用从用户处接收的变量。
Fallback Function
一个合约只能有一个Fallback Funciton
,这个函数没有函数名,external
作用域,不能接收参数(依旧可以使用msg
这个特殊的用户参数),当调用合约方法的时候,找不到一个方法名的时候,就会调用该方法(当合约接收eth的时候contract_address.transfer(1)
,就会调用这个方法,不过有一个前提,该Fallback Function
需要是payable
的)
函数重载
合约可以含有多个同名函数,但是要求是他们的参数类型要不一样,返回参数不作为参考标准。意思就是只要传送的参数能够没有歧义地调用正确函数就可以了,比如说contract A
就可以,他们参数个数不一样,但是contract B
却不行,因为address
变量跟合约都是20字节的地址变量(我们通过合约地址来表示合约)。
pragma solidity >=0.4.16 <0.6.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;
}
}
// This will not compile
contract B {
function f(C _in) public pure returns (C out) {
out = _in;
}
function f(address _in) public pure returns (address out) {
out = _in;
}
}
contract C {
}
抽象合约
当合约函数中,至少存在一个函数没有实现,那么这个合约就被称为抽象合约,它的作用是告诉别人,如果要从我这里派生新的合约,必须自己实现这个方法。
注:抽象合约不能被编译。
pragma solidity >=0.4.0 <0.6.0;
contract Feline {
function utterance() public returns (bytes32);
}
contract Cat is Feline {
function utterance() public returns (bytes32) { return "miaow"; }
}
interface
接口跟抽象合约类似,但是接口中的函数全部都不可以实现,并且不能有constructor
,不能有状态变量,函数必须都是external
,暂时不太清楚它作用是什么。
Library
顾名思义,函数库。设计出来的目的是为了代码重用,使一些相同的合约可以从函数库中调用,这样可以减少以太坊链上的合约代码量。比如,A合约跟B合约都使用了相同的函数f
,那么可以将f
放在library
中,然后让A和B调用,减少了代码冗余。
Using For
这个用起来就相当于Python中,类实例调用方法一样,实例作为第一个参数自动被传入,比如
pragma solidity >=0.4.16 <0.6.0;
library BigInt {
struct bigint {
uint[] limbs;
}
function fromUint(uint x) internal pure returns (bigint memory r) {
//...
}
function add(bigint memory _a, bigint memory _b) internal pure returns (bigint memory r) {
//...
}
function limb(bigint memory _a, uint _limb) internal pure returns (uint) {
//...
}
function max(uint a, uint b) private pure returns (uint) {
//...
}
}
contract C {
using BigInt for BigInt.bigint;
function f() public pure {
BigInt.bigint memory x = BigInt.fromUint(7);
BigInt.bigint memory y = BigInt.fromUint(uint(-1));
BigInt.bigint memory z = x.add(y); // 不用使用BigInt.add(x, y)的方式
assert(z.limb(1) > 0); // 同样不需要通过BigInt.limb(z, 1)的方式
}
}