function (
- 函数重载
- 函数重载是指函数命名相同,但需要满足以下两个条件:
- 函数传入参数的数量不同
- 函数传入参数的类型不同
- 若函数与多个参数均可以匹配,则会报错。例如 uint8 和 uint 均匹配时,报错
- 返回值的不同无效,其不能作为重载的依据
- address与uint160实际存储一致,故也会报错
- 函数重载是指函数命名相同,但需要满足以下两个条件:
pragma solidity ^0.4.17;
contract Overload{
//error,重名方法
function fun1(){}
function fun1(){}
//error,返回值不能作为判断依据
function fun2() returns(string){}
function fun2() returns(uint){}
//参数数量的不同可以作为重载的依据
function fun3(uint _num) {}
function fun3() {}
//编译通过,参数类型不同可以作为重载的依据,但是传参<256时会报错,无法匹配方法
function fun4(uint _num) {}
function fun4(uint8 _num) {}
//编译通过,但两者存储方式一致,故无法匹配。但自己实际的版本时候可行需要进一步验证。
function fun5(uint160 account) {}
function fun5(address account) {}
}
- 函数传入参数
- 可以使用类json的格式传入参数,顺序可以调整
- 在方法中调用其他方法时,必须传入所有参数,否则报错。但在外部调用多参数方法时,可以不必指定全部参数
pragma solidity ^0.4.17;
contract FuncParam{
uint public num;
string public name;
//在外部调用该多参数方法时,参数可以不全部指定
function setParam(uint _num,string _name) public{
num = _num;
name = _name;
}
//普通调用
function test1() public{
setParam(10,"yorick");
}
//使用类json的形式调用
function test2() public{
setParam({_name:"tom",_num:20});
}
//error,在方法中必须指定全部参数
function test3() public{
setParam(10);
}
}
- 函数参数返回
- solidity支持多参数返回,采用元组的形式
- returns中不是仅可以加类型,也可以添加变量。可以为该变量直接赋值。也可以通过return赋值。若两种方式都使用时,以return为标准。
pragma solidity ^0.4.17;
contract FuncReturn{
//returns后可以写变量名
function test1() pure public returns(uint a){
uint num = 10;
return num;
}
//可以为returns后面的变量直接赋值
function test2() pure public returns(uint a){
a = 20;
}
//两种赋值方式均存在的时候,以return为标准
function test3() pure public returns(uint a){
uint num = 10;
a = 20;
return num;
}
//多参数返回
function test4() pure public returns(uint a,uint b){
a = 10;
b = 20;
}
//多参数return返回
function test5() pure public returns(uint a,uint b){
return (10,20);
}
function test6(uint num1,uint num2) pure public returns(uint add,uint mul){
add = num1 + num2;
mul = num1 * num2;
}
function test7(uint num1,uint num2) pure public returns(uint add,uint mul){
return (num1+num2,num1*num2);
}
}
- 变量作用域
pragma solidity ^0.4.17;
contract ValueScope{
uint public num = 100;
//不能重定义
//uint num = 200;
//方法内部的重名变量覆盖了合约变量
function test1() pure public returns(uint) {
uint num = 10;
num = 11;
return num;
}
//传入的参数也属于方法内部的变量,故也覆盖了合约的重名变量
function test2(uint num) pure public returns(uint){
//以下均重定义 error
//uint num = 13;
//for(uint num=0 ; num<10 ; num++){}
{
//uint num = 10;
}
num = 12;
return num;
}
}
- 值传递
pragma solidity ^0.4.17;
contract ValueTransfer{
uint public num1 = 100;
//仅是拷贝num1中的值
uint public num2 = num1;
//由于是值的传递,修改num2的值不会改变num1的值
function changeNum() public{
num2 = 999;
}
//参数传递拷贝的是副本,副本的修改与原数据无关
function changeNum(uint _num2) pure public returns(uint){
_num2++;
return _num2;
}
function test() view public returns(uint){
return changeNum(num2);
}
}
-
constant关键字
- constant可以在函数声明的时候使用,相当于view,不消化gas。但是5.0以后废弃
- constant只能用于全局变量
- constant声明的值不可以修改
- constant可以用于uint,int,bytes,string类型的变量
-
构造函数
- 构造函数会在合约部署时自动调用一次
- 可以有很多用途,比如:初始化合约时候指定合约的拥有者
pragma solidity ^0.4.17;
contract FuncConstructor{
uint public a;
uint public b;
//构造函数初始化合约变量的值
constructor(uint _a,uint _b) public{
a = _a;
b = _b;
}
}
//同一个文件可以写多个合约,部署时候选择对应合约即可
contract FuncConstructor2{
address public owner;
//使用构造函数指定合约的拥有者地址
constructor() public{
owner = msg.sender;
}
}
- modifier函数
- 调用modifier函数后会将调用方法的代码插入到 _; 位置运行,并且在其前后插入modifier函数已经写好的代码
- 可以实现判断,赋值等操作
pragma solidity ^0.4.17;
contract ModifierTest{
address public owner;
uint public num;
constructor() public{
owner = msg.sender;
}
modifier OnlyOwner{
//判断 地址是否为合约拥有者的地址
require(msg.sender == owner);
//将调用方法的代码动态插入
_;
}
//使用modifier函数,仅需在后面添加该函数即可
function changeNum() OnlyOwner public {
num = 100;
}
}
//功能:实现仅当没有注册该地址的时候才会执行注册操作
//利用modifier做注册用户的判断操作
contract ModifierTest2{
mapping(address => uint) addressMapping;
mapping(uint => string) idMapping;
uint public count;
//在注册前先判断是否已经将该地址注册进合约中
modifier controlAddr{
require(addressMapping[msg.sender] == 0);
_;
}
//添加modifier函数的修饰
function register(string name) controlAddr public{
addressMapping[msg.sender] = ++count;
idMapping[count] = name;
}
function getId(address account) view public returns(uint){
return addressMapping[account];
}
function getName(uint id) view public returns(string){
return idMapping[id];
}
}
//功能:仅当目前level超过needLevel时,才会执行方法的内容
//利用带参数的modifier函数实现复杂控制
contract ModifierTest3{
uint public level = 5;
uint public normalSkill;
uint public superSkill;
modifier controlSkill(uint needLevel){
require(level >= needLevel);
_;
}
//向modifier函数传递参数
function changeNormalSkill() public controlSkill(2) {
normalSkill = 10;
}
//向modifier函数传递参数
function changeSuperSkill() public controlSkill(10) {
superSkill = 20;
}
}
//多重modifier的执行顺序,是嵌套的关系
//本例中,相当于mod1嵌套了mod2,即:mod1(mod2)
//执行顺序: num=1,num=3,num=100,num=4,num=2,最终num=2
contract ModifierTest4{
uint public num = 0;
modifier mod1{
num = 1;
_;
num = 2;
}
modifier mod2{
num = 3;
_;
num = 4;
}
function test() mod1 mod2 public {
num = 100;
}
}
- 合约继承
- 合约通过 is 关键字来继承上一个合约
- 合约可以连续继承,即:
- b is a,表示b继承了a
- c is b,表示c继承了b,同时也间接继承了a
pragma solidity ^0.4.17;
contract grandfater {
uint public house = 3;
function drink() pure public returns(string){
return "drink";
}
}
contract father is grandfater{
uint public money = 10000;
function learningSkill() pure public returns(string) {
return "learning skill";
}
}
contract son is father{
function getMoney() view public returns(uint){
return money;
}
function testLearningSkill() pure public returns(string){
return learningSkill();
}
function testDrink() pure public returns(string){
return drink();
}
}
- 修饰符权限
- 不同说明:
- internal只能在合约内部或者被继承的合约调用,合约外部不行
- external只能在合约外部调用,合约内部或者被继承的合约不行。若一定要在合约内部调用external修饰函数有两种方法:
- 可以使用this.函数名的方法在合约内部调用(this.函数名相当于通过合约地址调用该方法,类似于外部调用)
- 再声明一个合约,在新的合约内部创建或者引用该合约即可
- public合约内部,合约外部均可以调用
- private不能够被继承,在合约外部不能被调用,但是在合约内部可以被调用
- 什么是合约内部/外部?
- 在内部就是指合约内部可以调用这个函数
- 在外部就是指合约部署之后可以在旁侧看到这个函数的按钮
- 不同说明:
是否继承 | 变量 | 函数 |
---|---|---|
是 | public,internal | public,internal,external |
否 | private,external(变量实际没有external修饰符) | private |
pragma solidity ^0.4.17;
contract father{
//属性的不同权限修饰
uint a = 1;
uint public b = 2;
uint internal c = 3;
uint private d = 4;
//uint external e = 5;
//方法的不同权限修饰
function privateTest() pure private returns(string){
return "private";
}
function publicTest() pure public returns(string){
return "public";
}
function internalTest() pure internal returns(string){
return "internal";
}
function externalTest() pure external returns(string){
return "external";
}
}
contract son is father{
//在被继承的合约中,只能访问到public,internal和未设置,三类的属性值
function showa() view public returns(uint){
return a;
}
function showb() view public returns(uint){
return b;
}
function showc() view public returns(uint){
return c;
}
//私有属性无法访问
//function showd() view public returns(uint){
// return d;
//}
//在被继承的合约中,只能访问到public,internal以及使用this.函数名的external权限修饰的方法,而私有的方法无法访问
function getPublic() pure public returns(string){
return publicTest();
}
function getInternal() pure public returns(string){
return internalTest();
}
function getExternal() view public returns(string){
//error
//return externalTest();
return this.externalTest();
}
//私有方法无法访问
//function getPrivate() view public returns(string){
// return privateTest();
//}
}
//另外一种在合约内访问external修饰的方法
contract newExternal{
function getExternal() public returns(string){
father f = new father();
return f.externalTest();
}
}
- getter函数
- 如果在声明变量时使用public方法,那么合约会自动生成一个external类型的函数,返回值是public声明的值的类型,命名就是变量名。如果是mapping还会需要一个参数
- 如果我们在外面定义了这个函数,那么这个public变量自动生成的函数会自动消失
pragma solidity ^0.4.17;
contract Getter{
//自动生成对应的getter方法
uint public num = 233;
//mapping类型会生成一个带参数key的getter方法,可以通过该key参数获取对应的value
mapping(uint => string) public map;
//mapping的复杂定义,一个mapping映射到另一个mapping
mapping(uint => mapping(uint => string)) public complaxMap;
//向map中设置一个键值对
function setMapping() public {
map[2] = "yorick";
}
//向复杂map中设置一个键值对
function setComplaxMap() public {
complaxMap[1][3] = "hello world";
}
}
- 函数的重写
pragma solidity ^0.4.17;
contract Father{
uint public money = 233;
function learningSkill() pure public returns(string){
return "learning C/C++";
}
}
contract Son is Father{
//重写父类中的属性
uint public money = 666;
//重写父类中的方法
function learningSkill() pure public returns(string){
return "learning solidity";
}
//测试是否重写成功,成功则会返回子类的属性和方法返回值
function getContent() view public returns(uint,string){
return (money,learningSkill());
}
}
- 多重继承
pragma solidity ^0.4.17;
contract Father{
uint public height = 180;
function learningSkill() pure public returns(string){
return "learning C/C++";
}
}
contract Mother{
uint public height = 160;
function learningSkill() pure public returns(string){
return "learning Java";
}
}
//继承了两个合约,若两个合约具有相同属性或方法时,子合约继承最后一个合约的内容。
//若子合约定义同名属性或方法则覆盖父合约
contract Son is Father,Mother{
}
- 合约销毁
- 函数通过selfdistrust(合约调用者地址,实际上就是msg.sender)
- 销毁了合约之后,合约内的函数就会失效,无法再被调用
pragma solidity ^0.4.17;
contract DestructTest{
address owner;
uint public money;
constructor() public{
owner = msg.sender;
}
function increment() public{
money += 10;
}
function killContract() public{
if(msg.sender == owner){
selfdestruct(msg.sender);
}
}
}
函数总结:
private,不能被继承,不能在外部调用,可以在内部调用
internal,可以被继承,不能在外部调用,可以在内部调用
external,可以被继承,只能在外部调用,不能在内部调用(若需要强制调用,通过 "地址.")
public,权限最大,均可
pure,不会读取全局变量,也不会修改全局变量,不消耗gas
view,只读取全局变量的值,不修改,不消耗gas
constant,在函数上与view相同;在全局变量中,表示不能够修改该修饰的变量
payable,转账时候必须加的关键字,表示可以支付的
函数可以有多个返回值