zoukankan      html  css  js  c++  java
  • Solidity的delete操作

      Solidity中有个特殊的操作符delete用于释放空间,因为区块链技术做为一种公用资源,为避免大家滥用。且鼓励主动对空间的回收,释放空间将会返还一些gas。

      delete关键字的作用是对某个类型值a赋予初始值。比如如果删除整数delete a等同于a = 0

    一.删除基本类型
      对于区块链技术删除基本类型,使用delete会设置为对应的初始值:

      删除bool类型是false,变长字节数组是0x0。string则是空串。

    1.删除枚举
      删除枚举类型时,会将其值重置为序号为0的值。

    pragma solidity ^0.4.0;
    
    contract DeleteEnum {
        
        enum Light{RED, GREEN, YELLOW}
        
        Light light;
        
        function f() returns(Light) {
            light = Light.GREEN;
            delete light;
            return light;
        }
    }

      上面的例子中,删除light后,light将被置为序号为0的值。

    2.删除函数
      尝试了一下删除函数,会报错Error: Expression has to be an lvalue.,看来我们不能删除函数。

    二.删除复杂类型

    1.删除结构体
      删除一个结构体,会将区块链技术其中的所有成员变量一一置为初值,我们来看一个例子。

    pragma solidity ^0.4.0;
    
    contract Deletetruct {
        
        struct S{
            uint a;
            string b;
            bytes c;
        }
        
        S s;
        
        function delStruct() returns(uint, string, bytes) {
            s = S(10, "Hello world!", "abc");
            //删除结构体将重置所有元素
            delete s;
            return (s.a, s.b, s.c);
        }
    }

      在上面的例子中,我们声明了结构体s,调用delete s,结构体的值将变为其对应类型uint,string,bytes的初始值0,空串和0x0。

    2.删除映射
      映射是一个特殊的存在,由于映射的键并不总是能有效遍历(数据结构没有提供接口,也并不总是需要关心所有键是什么),所存的键的数量往往是非常大的,所以我们并不能直接删除一个区块链技术的映射。
            //Unary operator delete cannot be applied to type mapping(address => uint256) //delete map;
      如果直接删除一个映射会报错Unary operator delete cannot be applied
      但我们可以指定键来删除映射中的某一项:

    map[msg.sender] = 100; //可以按key删除映射 
    delete map[msg.sender]; 

    3.删除结构体中的映射 

      如果删除一个结构体时,其中含有映射类型,会跳过映射类型。我们来看一个删除含映射的结构体示例:

    pragma solidity ^0.4.0;
    
    contract MappingStructDelete {
        
        struct MapStruct{
            mapping(address => uint) m;
            uint i;
        }
        
        MapStruct ms;
        
        function delMapping() returns(uint, uint) {
            ms = MapStruct(200);
            ms.m[msg.sender] = 2000;
            
            //删除一个结构体
            delete ms;
            
            return (ms.m[msg.sender], ms.i);
        }
    }

      上面的示例中,删除结构体ms,并没有影响其中映射ms.m的值。

    三.删除数组
      对于定长数组,删除时,是将数组内所有元素置为初值。

    bytes32 by = "123"; 
    //0x0000000000000000000000000000000000000000000000000000000000000000 

      而对于变长数组时,则是将长度置为0。

    pragma solidity ^0.4.0;
    
    contract DeleteDynamicArray {
        
        function delDynamicArr() returns(uint) {
            uint[] memory a = new uint[](7);
            a[0] = 100;
            a[1] = 200;
            
            delete a;
            
            return (a.length);
        }
    }

      上述的代码a.length将返回长度为0。
    2.删除数组的一个元素
      我们也可以删除数组的一个元素,有一点违反直觉的是,删除一个元素后,数组会留个空隙在那里。比如三个元素的数组,删除了第二个元素,只是将第二个元素置为了初始值,其它没变。

    pragma solidity ^0.4.0;
    
    contract DeleteArrayEle {
        
        function delArrEle() returns(uint, uint, uint) {
            uint[] memory arr = new uint[](3);
            
            arr[0] = 1;
            arr[1] = 2;
            arr[2] = 3;
            
            delete arr[1];
            
            return (arr[0], arr[1], arr[2]);
        }
    }

      上述的代码运行后,将返回1,0,3。删除只是赋值,并没有移动元素。

    四.gas使用的考虑
      上文中,我们了解到,删除时会忽略映射,以及数组的某个元素被删除后,并不会自动整理数组。这些看起来很不符合常理,其实是基于对gas限制的考虑。因为如果映射或数组非常大的情况下,删除或维护它们将变得非常消耗gas。

      不过,清理空间,可以获得gas的返还。但无特别意义的数组的整理和删除,只会消耗更多gas,需要在业务实现上进行权衡。

    清理的最佳实践
      由于本身并未提供对映射这样的大对象的清理,所以存储并遍历它们来进行清理,显得特别消耗gas。一种实践就是能复用就复用,一般不主动清理。下面是一个数组的插入实现,比如增加一个计数器,直接忽略已使用过的位置。

      上面的例子中,我们在区块链技术数组新增时,直接忽略掉已使用过的槽位。而在代码内,我们使用numElements来代替array.length,以获取当前数组所在的位置。

      如果这种大对象是在某个事件发生时,一次性使用,然后需要回收的。一个更有效的方式是,在发生某个事件时,创建一个新合约,在新合约完成逻辑,完成后,让合约suicide。清理合约占用空间返还的gas就退还给了调用者,来节省主动遍历删除消耗的额外gas。

    五.删除的注意事项
      区块链技术删除本质是对一个变量赋初值。所以我们删除storage的引用时会报错,因为storage的引用并没有自己已分配的存储空间,所以不能对storage的引用直接赋初值。

    pragma solidity ^0.4.0;
    
    contract DeleteStorageRef {
        
        struct S{}
        
        S s;
        
        function DelStorageRef() {
            S storageRef = s;
            
            //Error: Unary operator delete cannot be applied to type struct DeleteStorageRef.S storage
            //delete storageRef;
            
            delete s;
        }
    }

      上面的例子中,删除storageRef会报错。

     文章来源:https://blog.csdn.net/qq_33764491/article/details/81386374

  • 相关阅读:
    3D照片放大展示窗口
    [NOI2015]品酒大会
    [SDOI2016]排列计数
    [SCOI2008]奖励关
    HDU4336 Card Collector
    CF540D Bad Luck Island
    [NOI2016]网格
    HDU3076 ssworld VS DDD
    [USACO10HOL]赶小猪
    CF113D Museum
  • 原文地址:https://www.cnblogs.com/flyingeagle/p/10142054.html
Copyright © 2011-2022 走看看