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

2023-12-13 15:03:35

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

1、函数重载:

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、函数命名参数:

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、函数返回值的特性:

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、值传递与副本拷贝:

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静态修饰:

pragma solidity ^0.5.17;

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

7、构造函数:

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)基本用法:

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:

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:

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的执行顺序:

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)基本的继承:

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)连续继承:

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)继承权限:

①合约属性的继承:

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修饰父类函数:

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修饰父类函数:

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修饰父类函数:

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修饰父类函数:

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方法:

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方法:

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映射:

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)属性重写:

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)函数重写:

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、多继承需要注意的情况:

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、合约的销毁(析构函数):

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);
? ? ? ? }
? ? } ? ?
}

文章来源:https://blog.csdn.net/Zevalin/article/details/134894111
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。