參考教程:【實戰篇】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
{//初始化結果為0uint 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號//給賬戶分配一個ididMapping[account] = id;//再將個人id與個人姓名進行映射綁定nameMapping[id] = name; }//根據賬戶地址獲取idfunction 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號//給賬戶分配一個ididMapping[account] = id;//再將個人id與個人姓名進行映射綁定nameMapping[id] = name; ? ? ? ?}//根據賬戶地址獲取idfunction 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);}} ? ?
}