基于以太坊的智能合约开发Solidity(函数&继承篇)

参考教程:【实战篇】1、函数重载_哔哩哔哩_bilibili

1、函数重载:

javascript 复制代码
pragma solidity ^0.5.17;

contract overLoadTest
{
    
    //不带参数
    function test() public
    {
        
    }
    
    //带一个参数
    function test(address account) public
    {
        
    }
    
    //参数类型不同,虽然uint160可以和address直接转化,但仍然满足重载的条件 
    function test(uint160 account) public
    {
        
    }
    
    //参数个数不同
    function test(uint160 account,address otherAccount) public
    {
        
    }
    
    // 此函数会编译报错,因为重载不考虑函数的返回值类型是否相同
    // function test(address account) returns(address sender){
    //     return msg.sender;
    // }
   
   uint public result = 0; 
    
    function negativeExample1(uint id) public
    {
        result = 100; 
    }
    
    function negativeExample1(uint8 id) public
    {
        result = 200;
    }
    
    function test1() public
    {
        //negativeExample1(1);  该语句会报错,因为传入函数的参数1既符合uint又符合uint8,        solidity无法辨别调用哪一个函数
    }

    function negativeExample2(address account) public
    {
        result = 100;
    }
    
    function negativeExample2(uint160 account) public 
    {
        result = 200;
    }

    function test2() public
    {
        //negativeExample2(1);  该语句会报错,address本质上就是一个uint160的一串数字,solidity无法辨别调用哪一个函数
    }
}

当一些行为模式一致,但是这个行为所输入的参数不一样时,便构成了重载,具体表现如下:

①函数的名字相同

②函数的参数不同(类型、数量)

③与函数的返回值无关

2、函数命名参数:

javascript 复制代码
pragma solidity ^0.5.17;

contract functionParamTest
{
    
   uint public id;
   string public name;
   
   function setParam(uint _id,string memory _name) public 
   {
       id = _id;
       name = _name;
   }
   
   function test() public
   {
       setParam(18,"xiaofang");  //正常地传参
   }
   
   function test2() public
   {
       setParam({_id:18,_name:"xiaofang"});  //通过形参名给函数传参
   }
   
   function test3() public
   {
       setParam({_name:"xiaofang",_id:18});  //顺序可以与定义函数参数时不同(在较多参数情况下命名参数对于代码的可读性有很好的提升)
   }  

   function test4() public
   {
       setParam(18);  //调用函数传参时不可缺少参数,否则编译会报错(在外部调用函数时可以缺少参数,但不建议缺少)
   }
}

3、函数返回值的特性:

javascript 复制代码
pragma solidity ^0.5.17;

contract functionReturnTest
{
    
    //最常规的返回值写法  
    function test() public view returns(uint)
    {
        return 10;
    }

    //返回值也可以进行一个命名,在returns后可以声明返回值的名称
    function test1() public view returns(uint result)
    {
        result = 1;
        return result;  //如果不写这条语句也是可以的,因为有声明过返回值的名称(但不建议搞花里胡哨)
    }
    
    //可以返回任意匹配类型的值(类型必须匹配,名称不匹配没关系)
    function test2() public view returns(uint result)
    { 
        uint a = 10;
        result = 100;
        return a;
    }
    
    //可以返回多个参数,在声明了返回值名称的情况下可以不用写return语句,但必须要给返回值赋值
    function test3(uint a,uint b) public view returns(uint add,uint multiply)
    {
        add = a+b;
        multiply = a*b;
    }
    
    //返回多个参数时return语句要加括号
    function test4(uint a,uint b) public view returns(uint add,uint  multiply)
    {
        return (a+b,a*b);
    }
    
    //可以利用多返回值的特性直接进行数据交换操作
    function test5(uint a,uint b) public view returns(uint _b, uint _a)
    {
        return (b,a);
    }
    
}

4、变量的生命周期与作用域:

(1)作用域:在程序中有非常多的花括号,变量定义在哪对花括号中,那么这对花括号所包含的就是变量的作用域,变量可以在其作用域内有效使用。(注意,函数形参的作用域是函数内部,虽然它看起来不是定义在函数体内部)

(2)生命周期:从变量被分配空间到空间被收回的这一个时间段,称为变量的生命周期,变量的生命周期自其被定义开始,至其作用域尾部结束(没有进行手动销毁的话)。

5、值传递与副本拷贝:

javascript 复制代码
pragma solidity ^0.5.17;

contract transferValueTest
{
    
    uint public a = 200;
    uint public b = a;   //将a的值赋给b,但是a和b各自占有不同的空间
    
    function changIt() public
    {
        b = 400;  //改变b的值,并不影响a
    }
    
    //i作为形式参数,当调用该函数时,是将所传值的副本传入的,并不会改变所传引用的值,也就是说,对i做修改并不影响a的值
    function changeIt3(uint i) public returns(uint)
    {
        i++;
        return i++;
    }
    
    function test() public 
    {
        changeIt3(a);
    }
  
}

6、const静态修饰:

javascript 复制代码
pragma solidity ^0.5.17;

contract constantTest
{
    // 在0.4版本中,constant等同于view,在0.5以上的版本中已经废弃
    /*
    function test() public constant returns(uint)
    {
        return 100;
    }
    */
}

7、构造函数:

javascript 复制代码
pragma solidity ^0.5.17;

contract constructorTest
{
    //初始化结果为0
    uint public a;
    //构造函数的名称与合约名相同,在合约部署(或者被创建)的时候执行(0.5以下版本支持该种语法)
    /*
    function constructorTest()
    {
        a = 100;
    }
    */
            
    //一个合约内部不能有多个构造函数
    /*
    function constructorTest(uint _a,uint _b){
        a = _a;
    }
    */
    //在0.5以上版本中,solidity采用了关键字constructor来定义构造函数
    constructor() public
    {
        a = 100;
    }

    /*
    address public ower;
     
    function constructorTest()  在合约部署时,可以借助构造函数传递一些内容到合约内,例如部署合约的账户
    {
        ower = msg.sender;
    }
    */
}

8、函数修改器modifire:

(1)基本用法:

javascript 复制代码
pragma solidity ^0.5.17;

contract modifireTest
{
    address public ower;
    
    uint public num = 0;
    
    constructor() public
    {
        ower = msg.sender;
    }
    
    //定义一个函数修改器 
    modifier onlyOwer
    {
        require(msg.sender == ower);  //不符合条件时则抛出异常,停止执行下面的语句并回滚
        //修饰函数时,函数相当于被插入到'_;'位置,然后把onlyOwer当作函数执行
        _;
    }
    
    function changeIt(uint _num) public onlyOwer
    {
        num = _num;  //该例中只有部署该合约的账户才有权利调用这个函数,其它账户调用该函数将会报错
       //当然,"require(msg.sender == ower);"也可以直接写在该函数中,这样就不需要借助modifier
    }
    
}

Solidity的异常处理:

①Solidity使用"状态恢复异常"来处理异常。这样的异常将撤消对当前调用(及其所有子调用)中的状态所做的所有更改,并且向调用者返回错误。

②函数assert和require可用于判断条件,并在不满足条件时抛出异常

• assert() 一般只应用于测试内部错误,并检查常量

• require() 应用于确保满足有效条件,或验证调用外部合约的返回值

• revert() 用于抛出异常,它可以标记一个错误并将当前调用回退

(2)运用示例1:

javascript 复制代码
pragma solidity ^0.5.17;

contract mappingTest
{
    //一个钱包地址对应一个个人身份id 
    mapping(address => uint) idMapping;
    //一个人的身份id地址对应一个人的姓名
    mapping(uint => string) nameMapping;
    uint id = 0;
    address public ower;

    //定义一个函数修改器 
    modifier onlyOwer
    {
        require(idMapping[msg.sender] == 0);  //判断调用函数的账户有没有绑定身份的记录,以免同一账户注册多个身份
        //修饰函数时,函数相当于被插入到'_;'位置,然后把onlyOwer当作函数执行
        _;
    }

    function register(string memory name) public onlyOwer //该函数模拟新账户绑定身份
    {
        address account = msg.sender;  //调用该函数的账户为需要绑定身份和生成id号的新账户
        id++;  //这里的id号应该是随机生成,总之每个调用该函数的账户都应该获得不同的id号
        //给账户分配一个id
        idMapping[account] = id;
        //再将个人id与个人姓名进行映射绑定
        nameMapping[id] = name; 
    }
    //根据账户地址获取id
    function getIdByAddress(address account) public view returns(uint)
    {
        return idMapping[account];
    }
    //根据id获取个人姓名
    function getNameById(uint id) public view returns(string memory)
    {
        return nameMapping[id];
    }

(3)运用示例2:

javascript 复制代码
pragma solidity ^0.5.17;

contract modifireTest
{
    uint level = 200;
    uint money = 0;
    
    modifier modifyLevel(uint _inputLevel)  //函数修改器可以有参数,这样使用起来更加灵活
    {
        
        require(level >= _inputLevel, "Your level is not within the range!");
       //如不满足require中的条件,错误类型为Your level is not within the range
        _;
    }
    
    function addMoney() modifyLevel(50) public 
    {
        //require(level >= 50);  五十级以上才能调用该函数
        money = 100;
    }
    
    function recoverCash() modifyLevel(200) public 
    {
        //require(level >= 200);  两百级以上才能调用该函数
        money = 200;
    }
    
    function getMoney() public view returns(uint) 
    {
        return money;
    }
    
}

(4)多重modifire的执行顺序:

javascript 复制代码
pragma solidity ^0.5.17;

contract modifireTest4
{
    uint public a = 0;
    
    modifier mod1
    {
        a = 1;  //执行顺序------1(最先执行)
        
        _;
        
        a = 2;  //执行顺序------5(最后执行)
    }
    
    modifier mod2
    {
        a = 3;  //执行顺序------2
        
        _;
        
        a = 4;  //执行顺序------4
    }
    
    function test() public mod1 mod2  //调用该函数后,a的值为2
    {
        a = 100;  //执行顺序------3
    }
    
}

9、合约的继承:

(1)基本的继承:

javascript 复制代码
pragma solidity ^0.5.17;

contract Father
{
    uint money = 10000;
    
    function noSmoking() public view returns(string memory)
    {
        return "I'm not somking";
    }
}

contract Son is Father  //继承语法:contract 子类名称 is 父类名称1,父类名称2......
{
    function getMoney() public view returns(uint)
    {
        return money;  //子类继承了父类中的属性
}

    function test() public view returns(string memory)
    {
        return noSmoking();  //子类继承了父类中的函数
    }
}

(2)连续继承:

javascript 复制代码
pragma solidity ^0.5.17;

contract GrandFather
{
    uint height = 170;
}

contract Father is GrandFather
{
    uint money = 10000;
    function getHeight() public view returns(uint)
    {
        return height;  //子类继承了父类中的属性
    }
}

contract Son is Father
{
    function getHeight() public view returns(uint)
    {
        return height;  //子类继承了父类的父类中的属性
    }
}

(3)继承权限:

①合约属性的继承:

javascript 复制代码
pragma solidity ^0.5.17;

contract Father
{

    uint private privateMoney = 2000;  //加上private标识的属性或函数不会被子类继承
    //其余情况下可以被继承
    //uint privateMoney = 2000;
    //uint public privateMoney = 2000;
    //uint internal privateMoney = 2000;
    
    //注意:external不能修饰属性,只修饰函数
    
    uint money = 10000;
    
    function noSmoking() public view returns(string memory)
    {
        return "I'm not somking";
    }
    
}

contract Son is Father
{
    
    function getMoney() public view returns(uint)
    {
        return money;  //正常"财产"可以被继承
}

    function getPrivateMoney() public view returns(uint)
    {
        //return privateMoney;  父类的私房钱子类无法继承
}

}

②public修饰父类函数:

javascript 复制代码
pragma solidity ^0.5.17;

contract Father
{
    
    function noSmoking() public view returns(string memory)
    {
        return "I'm not somking";
    }
    
}

contract Son is Father
{
    
    function test() public view returns(string memory)
    {
        return noSmoking();  //子合约可以正常调用父类中public修饰的函数
    }
    
}

③private修饰父类函数:

javascript 复制代码
pragma solidity ^0.5.17;

contract Father
{
    
    function noSmoking() private view returns(string memory)
    {
        return "I'm not somking";
    }
    
}

contract Son is Father
{
    
    function test() public view returns(string memory)
    {
        //return noSmoking();  子合约不可以调用父类中private修饰的函数
    }
    
}

④internal修饰父类函数:

javascript 复制代码
pragma solidity ^0.5.17;

contract Father
{
    
    function noSmoking() internal pure returns(string memory)
    {
        return "I'm not somking";
}

    function test() public returns(string memory)
    {
        return noSmoking();  //被internal修饰的函数,只能在合约内部以及子合约中被调用,外部无法直接调用
    }
    
}

contract Son is Father
{

    function test2() public pure returns(string memory)
    {
        return noSmoking();  //子合约可以正常调用父类中internal修饰的函数(当然,也只能在合约内部调用,外部不可见)
    }
    
}

⑤external修饰父类函数:

javascript 复制代码
pragma solidity ^0.5.17;

contract Father
{
    
    function noSmoking() external pure returns(string memory)
    {
        return "I'm not somking";
    }

    function test() public returns(string memory)
    {
        //return noSmoking();  被external修饰的函数,只能在外部被调用,合约内部以及子合约中无法直接调用
        return this.noSmoking();  //通过this可以让编译器认为这是在外部通过该合约地址调用该函数,这样就不会报错
    }
    
}

contract Son is Father
{

    function test2() public view returns(string memory)  //使用this的话,千万不要使用pure
    {
        //return noSmoking();
        return this.noSmoking();  //子类也可以通过this调用父类中external修饰的函数
    }
    
}

contract Mother
{

    function test() public returns(string memory)
    {
        Father f = new Father();  //对于被external修饰的函数,在其它非子合约中可以直接创建合约对象,通过合约对象进行外部调用
        return f.noSmoking();
    }
    
}

10、全局变量自动getter函数:

(1)普通变量的get方法:

javascript 复制代码
pragma solidity ^0.5.17;

contract GetterTest
{
    
    uint public num = 100;  //public修饰符修饰的属性,默认会生成一个get方法,供我们外部调用
    
    /*
    function num() external returns(uint)  
    {
       return num;
    }
    1、变量用public修饰后,这个函数就是生成的get方法(函数体内部定义的变量不能用public修饰)
    2、默认生成的get函数是external权限的,不能够在合约的内部调用
    3、对于该例,num()方法只能有一个,要是自己进行重写,就会覆盖默认生成的get方法
    */
    
     function test() public
     {
        // num();
        this.num();  //可以通过this对get方法进行外部调用
        
    }
    
}

(2)mapping类型数据的get方法:

javascript 复制代码
pragma solidity ^0.4.18;  //编译器版本与前面例子所用的不同

contract GetterTest
{
    
mapping(uint => string) public map;

    function map(uint key) external returns(string)  //map的get方法(需传入key值才能获取value值,对于嵌套的mapping,就需要传入多个key值)
    {
        return map[key];
    }
    
}

补充------mapping映射:

javascript 复制代码
pragma solidity ^0.5.17;

contract mappingTest
{
    //个人认为这个有点像Python中的字典(可以理解为mapping(key => value) 字典名)
    //一个钱包地址对应一个个人身份id 
    mapping(address => uint) idMapping;
    //一个人的身份id地址对应一个人的姓名
    mapping(uint => string) nameMapping;
  
    uint id = 0;
    function register(string memory name) public //该函数模拟新账户绑定身份
    {
        address account = msg.sender;  //调用该函数的账户为需要绑定身份和生成id号的新账户
        id++;  //这里的id号应该是随机生成,总之每个调用该函数的账户都应该获得不同的id号
        //给账户分配一个id
        idMapping[account] = id;
        //再将个人id与个人姓名进行映射绑定
        nameMapping[id] = name;        
    }
    //根据账户地址获取id
    function getIdByAddress(address account) public view returns(uint)
    {
        return idMapping[account];
    }
    //根据id获取个人姓名
    function getNameById(uint id) public view returns(string memory)
    {
        return nameMapping[id];
}

    mapping(uint => mapping(uint => mapping(uint => string))) public map;
    function test() public
    {
        map[0][1][1] = "lalalalala";  //mapping数据类型还可以嵌套,相当于Python中的多维字典
        //map[0]:mapping(uint => mapping(uint => string))
        //map[0][1]:mapping(uint => string)
        //map[0][1][1]:string
    }
   
}

mapping(key => value) Mapping的相关语句:

(1)"mapping(type1 => type2) Mapping;"的作用是创建一个type1类型到type2类型的映射,映射组的名称为"Mapping"。

(2)"Mapping[key] = value;"的作用是:

①如果Mapping组中此前没有key这个键值,那么Mapping组中会添加key这个键值到value的映射。

②如果Mapping组中此前存在key这个键值,那么key键值的映射修改为value。

11、继承中的重写:

(1)属性重写:

javascript 复制代码
pragma solidity ^0.5.17;

contract Father
{
    
   uint money = 10000;
   uint public height = 170;  //父类属性用public修饰,那么子类继承后会生成get方法,调用该方法会获取父类的height而不是子类的
    
}

contract Son is Father
{
    
    uint money = 20000;  //子类重写了父类的money,以子类为准(但是不影响父类的money)
    uint height = 180;  //子类重写了父类的height,那么子类使用height时将以此为准(如果用public修饰该属性,那么此处生成的get方法会将父类生成的get方法覆盖,调用该方法会获取子类的height)
    
    function getMoney() public view returns(uint)
    {
        return money;
    }

    function getHeight() public view returns(uint)
    {
        return height;
}

}

(2)函数重写:

javascript 复制代码
pragma solidity ^0.5.17;

contract Father
{
   uint public money = 10000;
   
   function noSmoking() public view returns(string memory)
   {
       return "I don't smoke,and I do not drink";
   }
}

contract Son is Father
{
    uint public money = 20000;
    
    function noSmoking() public view returns(string memory)  //子类重写了父类的函数,父类函数将会被覆盖
    {
        return "I do not smoke,but I do drink";
    }
    
    function test() public view returns(string memory)
    {
        return noSmoking();
    }
}

12、多继承需要注意的情况:

javascript 复制代码
pragma solidity ^0.5.17;

contract Father
{
    
   uint public money = 10000;
   
   uint public height = 180;
    
}

contract Mother
{

    uint public money = 20000;
    
    uint public height  = 170;
    
    uint public weight = 120;
    
}

contract Son is Father,Mother
{
    function test() public view returns(uint)
    {
        return height;  //会返回Mother中的height,后继承的属性如果与前面继承的相同,前继承的属性将会被覆盖
    }
    
}

13、合约的销毁(析构函数):

javascript 复制代码
pragma solidity ^0.5.17;

contract DestructTest
{
    
    address ower;
    
    constructor() public
    {
        ower = msg.sender;
    }

    uint public money = 100;
 
    function increment() public
    {
        money += 100;
    }
    
    function kill() public
    {
        if(msg.sender == ower)  //只有发布合约的账户有权利销毁合约
        {
            //手动进行自我销毁
            selfdestruct(msg.sender);
        }
    }    
}
相关推荐
区块链蓝海8 小时前
Secured Finance携手Axelar及Squid提升流动性,迎接USDFC主网
区块链
yunteng52117 小时前
solidity之Foundry安装配置(一)
web3·区块链·solidity·foundry
qiquandongkh21 小时前
2025年股指期货和股指期权合约交割的通知!
大数据·金融·区块链
yoona10202 天前
Rust编程语言入门教程 (七)函数与控制流
开发语言·rust·区块链·学习方法
MetaverseMan3 天前
从sumsub获取用户图片
区块链
电报号dapp1194 天前
区块链虚拟币资产去中心化私钥钱包开发
人工智能·去中心化·区块链·智能合约
漠缠4 天前
股票与比特币投资困境分析及解决方案
人工智能·区块链
AC使者4 天前
解释区块链技术的应用场景和优势。
区块链
YSGZJJ4 天前
怎么查股指期货持仓量?
区块链