以太坊智能合约学习笔记(三)

Mapping

映射(Mappings):类似于哈希表。mapping 中任何一个可能的 key 都对应着一个 value,它的默认值是default-value 。底层用的不是使用链表 + 数组实现的,不需要扩容。value 引用存储在 keccak256(key) 表姐地址,在 storage 上存储,理论无限大。这也导致了,无法原生地遍历 mapping。另外 mapping 只能做状态变量,不能做本地局部变量。

语法

// 声明映射
// key 类型包括: bool, int/uint , address, string...
// value 类型包括: any type
mapping(address => uint) balances; // 状态变量

function() {
    mapping(address => uint) balances; // 报错。本地局部变量报错。
}

// 成员赋值
balances[0x1] = 1;

// 成员取值
balances[0x1] // 1
balances[0x2] // 默认值 0
balances[0x3] // 默认值 0

可视度

可视度(Visibility): 可视度不是说别人无法用肉眼无法看到,智能合约部署在区块链上,代码任何人都可以看见。可视度指的是,决定函数或者状态变量的可以被哪些智能合约可见和调用。

  • public: 所有智能合约可见
  • external: 只能被外部合约或者外部调用者可见
  • internal: 外部合约不可见,当前合约内部和子类合约可见
  • private: 只有当前合约可见

可以用一个例子来解释,什么是外部合约、外部调用者、当前合约内部、子类合约。

有三个合约,ChildParentOutSide。其中 Child 继承 ParentOutSide 和另外两个合约没有直接关系。

- 外部合约可见: `Child` 的函数可以调用 `OutSide` 的函数。
- 外部调用者可见: 在 remix 的 run 面板中 create `Child` 合约后,可以调用。
- 当前合约内部可见: `Child` 的函数可以调用 `Child` 自身的其他函数。
- 子类合约可见:`Child` 的函数可以调用 `Parent` 的函数。

ChildParentOutSide 都有以下四个函数:

function publicFunc() public {}

function externalFunc() external {}

function internalFunc() internal {} // 不可调用

function privateFunc() private {} // 不可调用

// `Child` 中的函数名都加了 `self` 前缀,和 Parent 中的函数做区分。

函数可见性public(default) , external , internal , private

具体示例代码如下:

pragma solidity ^0.4.14;

import "./parent.sol";
import './outside.sol';

contract Child is Parent{
        

    // 返回一个值
    function testOutside() {
        Outside o = new Outside();
        
        o.publicFunc();
        o.externalFunc();
        // o.internalFunc();  // 报错。Child 不能调用 OutSide 的 internal 函数。
        // o.privateFunc();   // 报错。Child 不能调用 OutSide 的 private 函数。
    }
    
    function testParent () {

        publicFunc();
        // externalFunc();   // 报错。Child 不能调用 Parent 的 external 函数。
        internalFunc();
        // privateFunc();    // 报错。Child 不能调用 Parent 的 private 函数。
    }
    
    function selfPublicFunc() public {}
    
    function selfExternalFunc() external {}
    
    function selfInternalFunc() internal {} // remix 不可见。外部调用者不能调用 internal 函数。
    
    function selfPrivateFunc() private {} // remix 不可见。外部调用者不能调用 private 函数。
}

状态变量可见性public , internal(default) , private 没有 external

状态变量可见度和函数可见度非常相似,只有一点需要特殊说明。当在状态变量上添加可视度时,编译器会给状态变量添加个 getter 函数,方便外部直接获取。

使用 remix 创建以下合约时,会出现一个 data 函数,点击该函数就可以获取到 data 的值。

<图>

继承(待补充…)

函数修饰器

函数修饰器(Function Modifiers): 函数修饰器可以改变函数的行为。可通过 _ 来控制修饰器的执行时机。

在 Example1 中,通过 modifier 来抽象出检查条件的代码,并修饰 input2。让 input2 实现和 inputs2 一样的行为。其中 _ 可以简单的理解为,do something 代码在 modifier 之后执行。

contract Example1 {
    address owner;

    function Example() public { owner = msg.sender; }

    modifier onlyOwner {
        require(msg.sender == owner);
        _;
    }

    function inputs1() {
        require(msg.sender == owner);
        // do something...
    }

    function inputs2() onlyOwner {}
}

例如:函数修饰器可以在执行自动执行指定语句。inputs1inputs2 在行为上是等价的。

在 Example2 中,通过 modifier 来抽象出状态变量自增的代码,并修饰 input2。让 input2 实现和 inputs2 一样的行为。其中 _ 可以简单的理解为,do something(不包括 return) 代码在其之前执行。

contract Example2 {
    // 省略 ...
    uint output;

    modifier increase return(unit) {
        _;
        output++;
    }

    function change1() return(unit) {
        // do something...
        output++;
        return output;
    }

    function change2() increase {
        // do something...
        return output;
    }
}

Safe Math 和 Library(待完善…)

因为 Solidity 数字大多数是和钱有关的,所以在加减乘除的四则运算时,发生上溢或下溢是非常危险的。

contract Test {
    uint8 public a = 0;

    function set () {
        a -= 100;
    }


    function set2 () {
        unit8 c = a - 100;
        assert(c < a);
        a = c;
    }

}

-----

set() // a = 156 整型溢出
import './SafeMath.sol'; // 引入 unit8

contract Test {
    uint8 public a = 0;

    function set () {
        a = SafeMath.sub(a, 100);
    }
}
import './SafeMath.sol'; // 引入 unit8


contract Test {
    using SafeMath for uint8; // 将 SafeMath 库里的方法,赋予了 uint8 类型。
    uint8 public a = 0;

    function set () {
        a = a.sub(100);
        // a = SafeMath.sub(a, 100);
    }
}

其他

函数(function)声明:函数是合约内代码的可执行单元。

contract SimpleStorage {
    function simpleFn() {
    }
}

函数返回值

  • 返回一个或多个参数
  • 直接赋值返回的命名参数,并返回
    // 返回一个值
    function oneParameter() returns(uint){
        return 1;
    }
    
    // 返回多个返回值
    function twoParameters() returns(uint, uint) {
        return (1,2);
    }
    
    // 命名参数直接赋值
    function namedParameter() returns(uint foo, uint bar) {
        foo = 1;
        bar = 2;
    }
    
    // 获取返回值 
    function get() {
        uint one = oneParameter();
        var (two1, two2) = twoParameters();
        var (named1, named2) = namedParameter();
    }

本文由【区块链研习社】优质内容计划支持,更多关于区块链的深度好文,请点击【区块链研习社】简书专栏:区块链研习社简书专栏

    原文作者:穿越过来的键盘手
    原文地址: https://segmentfault.com/a/1190000013843334
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞