繼承
當(dāng)我們的合約代碼越來(lái)越長(zhǎng)蜒滩。 當(dāng)代碼過(guò)于冗長(zhǎng)的時(shí)候,最好將代碼和邏輯分拆到多個(gè)不同的合約中捡遍,以便于管理画株。
有個(gè)讓 Solidity 的代碼易于管理的功能谓传,就是合約 inheritance (繼承):
當(dāng)一個(gè)合約繼承自多個(gè)合約時(shí)续挟,只會(huì)在區(qū)塊鏈上創(chuàng)建單個(gè)合約诗祸,并將所有父合約中的代碼復(fù)制到創(chuàng)建的合約中直颅。
Solidity的繼承與Python非常相似肘习,特別是多繼承漂佩。
contract Doge {
function catchphrase() public returns (string) {
return "So Wow CryptoDoge";
}
}
contract BabyDoge is Doge {
function anotherCatchphrase() public returns (string) {
return "Such Moon BabyDoge";
}
}
由于 BabyDoge 是從 Doge 那里 inherits (繼承)過(guò)來(lái)的。 這意味著當(dāng)你編譯和部署了 BabyDoge养葵,它將可以訪問(wèn) catchphrase() 和 anotherCatchphrase()和其他我們?cè)?Doge 中定義的其他公共函數(shù)关拒。
這可以用于邏輯繼承(比如表達(dá)子類的時(shí)候佃蚜,Cat 是一種 Animal)。 但也可以簡(jiǎn)單地將類似的邏輯組合到不同的合約中以組織代碼着绊。
父構(gòu)造函數(shù)的參數(shù)
派生的合約需要為父構(gòu)造函數(shù)提供所有的參數(shù)谐算。有兩種方式:
pragma solidity ^0.4.14;
contract Base {
uint x;
function Base(uint _x) public { x = _x; }
}
contract Derived is Base(7) {
function Derived(uint _y) Base(_y * _y) public {
}
}
- 第一種是直接在繼承列表里實(shí)現(xiàn)is Base(7)
- 第二種是在派生的構(gòu)造器的頭部,修飾符被調(diào)用時(shí)實(shí)現(xiàn)Base(_y * _y)归露。
使用原則:
- 如果構(gòu)造函數(shù)參數(shù)是一個(gè)常量洲脂,并且定義了合約的行為或描述了它的行為,第一種方式比較方便剧包。
- 如果父構(gòu)造函數(shù)參數(shù)依賴于派生合約的構(gòu)造函數(shù)恐锦,則必須使用第二種方法。
- 如果在這個(gè)荒謬的例子中疆液,這兩個(gè)地方都被使用一铅,修飾符樣式的參數(shù)優(yōu)先。
當(dāng)我們多個(gè)合約為多個(gè)文件的時(shí)候我們?cè)撛趺蠢^承潘飘?
當(dāng)你有多個(gè)文件并且想把一個(gè)文件導(dǎo)入另一個(gè)文件時(shí),可以使用 import 語(yǔ)句:
import "./someothercontract.sol";
contract newContract is SomeOtherContract {
}
這樣當(dāng)我們?cè)诤霞s(contract)目錄下有一個(gè)名為 someothercontract.sol 的文件( ./ 就是同一目錄的意思),它就會(huì)被編譯器導(dǎo)入现喳。
多繼承
import "./someothercontract.sol"
import "./othercontract.sol";
contract newContract is SomeOtherContract,othercontract {
}
多繼承順序:
在is指令中給出父類的順序很重要幌缝。在下面的代碼中荒叼,Solidity會(huì)報(bào)錯(cuò):“Linearization of inheritance graph impossible”萝玷。
// 以下代碼無(wú)法編譯
pragma solidity ^0.4.14;
contract X {}
contract A is X {}
contract C is A, X {}
------------------------------------------------------
// 以下代碼可以編譯通過(guò)
pragma solidity ^0.4.14;
contract X {}
contract A is X {}
contract C is X, A {}
原因是C要求X來(lái)重寫(xiě)A(定義A,X這個(gè)順序),但A本身的要求重寫(xiě)X,這是一個(gè)矛盾辽装,不能解決丰涉。
storage 和 memory
在 Solidity 中,有兩個(gè)地方可以存儲(chǔ)變量 —— storage 或 memory
Storage 變量是指永久存儲(chǔ)在區(qū)塊鏈中的變量。 Memory 變量則是臨時(shí)的加袋,當(dāng)外部函數(shù)對(duì)某合約調(diào)用完成時(shí),內(nèi)存型變量即被移除。 你可以把它想象成存儲(chǔ)在你電腦的硬盤(pán)或是RAM中數(shù)據(jù)的關(guān)系。
大多數(shù)時(shí)候你都用不到這些關(guān)鍵字拭卿,默認(rèn)情況下 Solidity 會(huì)自動(dòng)處理它們。 狀態(tài)變量(在函數(shù)之外聲明的變量)默認(rèn)為“存儲(chǔ)”形式,并永久寫(xiě)入?yún)^(qū)塊鏈;而在函數(shù)內(nèi)部聲明的變量是“內(nèi)存”型的呐馆,它們函數(shù)調(diào)用結(jié)束后消失收班。
然而也有一些情況下舵变,你需要手動(dòng)聲明存儲(chǔ)類型,主要用于處理函數(shù)內(nèi)的 結(jié)構(gòu)體 和 數(shù)組 時(shí):
contract SandwichFactory {
struct Sandwich {
string name;
string status;
}
Sandwich[] sandwiches;
function eatSandwich(uint _index) public {
// Sandwich mySandwich = sandwiches[_index];
// ^ 看上去很直接,不過(guò) Solidity 將會(huì)給出警告
// 告訴你應(yīng)該明確在這里定義 `storage` 或者 `memory`住涉。
// 所以你應(yīng)該明確定義 `storage`:
Sandwich storage mySandwich = sandwiches[_index];
// ...這樣 `mySandwich` 是指向 `sandwiches[_index]`的指針
// 在存儲(chǔ)里柳爽,另外...
mySandwich.status = "Eaten!";
// ...這將永久把 `sandwiches[_index]` 變?yōu)閰^(qū)塊鏈上的存儲(chǔ)
// 如果你只想要一個(gè)副本,可以使用`memory`:
Sandwich memory anotherSandwich = sandwiches[_index + 1];
// ...這樣 `anotherSandwich` 就僅僅是一個(gè)內(nèi)存里的副本了
// 另外
anotherSandwich.status = "Eaten!";
// ...將僅僅修改臨時(shí)變量俩功,對(duì) `sandwiches[_index + 1]` 沒(méi)有任何影響
// 不過(guò)你可以這樣做:
sandwiches[_index + 1] = anotherSandwich;
// ...如果你想把副本的改動(dòng)保存回區(qū)塊鏈存儲(chǔ)
}
}