溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Solidity語法的重載,繼承的定義是什么

發(fā)布時間:2021-12-07 15:27:48 來源:億速云 閱讀:157 作者:iii 欄目:互聯(lián)網(wǎng)科技

本篇內容介紹了“Solidity語法的重載,繼承的定義是什么”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!

1

摘要

以太坊智能合約語言Solitidy是一種面向對象的語言。

2

合約說明

Solidity 合約類似于面向對象語言中的類。合約中有用于數(shù)據(jù)持久化的狀態(tài)變量,和可以修改狀態(tài)變量的函數(shù)。 調用另一個合約實例的函數(shù)時,會執(zhí)行一個 EVM 函數(shù)調用,這個操作會切換執(zhí)行時的上下文,這樣,前一個合約的狀態(tài)變量就不能訪問了。

面向對象(Object Oriented,OO)語言有3大特性:封裝,繼承,多態(tài),Solidity語言也具有著3中特性。

面向對象語言3大特性的說明解釋如下:

  • 封裝(Encapsulation)

    封裝,就是把客觀事物封裝成抽象的類,并且類可以把自己的數(shù)據(jù)和方法只讓可信的類或者對象操作,對不可信的進行信息隱藏。一個類就是一個封裝了數(shù)據(jù)以及操作這些數(shù)據(jù)的代碼的邏輯實體。在一個對象內部,某些代碼或某些數(shù)據(jù)可以是私有的,不能被外界訪問。通過這種方式,對象對內部數(shù)據(jù)提供了不同級別的保護,以防止程序中無關的部分意外的改變或錯誤的使用了對象的私有部分。

  • 繼承(Inheritance)

    繼承,指可以讓某個類型的對象獲得另一個類型的對象的屬性的方法。它支持按級分類的概念。繼承是指這樣一種能力:它可以使用現(xiàn)有類的所有功能,并在無需重新編寫原來的類的情況下對這些功能進行擴展。 通過繼承創(chuàng)建的新類稱為“子類”或“派生類”,被繼承的類稱為“基類”、“父類”或“超類”。繼承的過程,就是從一般到特殊的過程。要實現(xiàn)繼承,可以通過 “繼承”(Inheritance)和“組合”(Composition)來實現(xiàn)。繼承概念的實現(xiàn)方式有二類:實現(xiàn)繼承與接口繼承。實現(xiàn)繼承是指直接使用 基類的屬性和方法而無需額外編碼的能力;接口繼承是指僅使用屬性和方法的名稱、但是子類必須提供實現(xiàn)的能力。

  • 多態(tài)(Polymorphism)

    多態(tài),是指一個類實例的相同方法在不同情形有不同表現(xiàn)形式。多態(tài)機制使具有不同內部結構的對象可以共享相同的外部接口。這意味著,雖然針對不同對象的具體操作不同,但通過一個公共的類,它們(那些操作)可以通過相同的方式予以調用。

    另外也解釋一下重載和重寫。

    重載(Override)是多態(tài)的一種形式,是一個類的內部,方法中多個參數(shù),根據(jù)入?yún)⒌膫€數(shù)不同,會返回不同的結果。

    重寫(Overwrited),是子類繼承父類,重寫父類的方法。多態(tài)性是允許你將父對象設置成為一個或更多的他的子對象相等的技術,賦值之后,父對象就可以根據(jù)當前賦值給它的子對象的特性以不同的方式運作。簡單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針。多態(tài)性在Object Pascal和C++中都是通過虛函數(shù)的。

3

函數(shù)重載(Override)

合約可以具有多個不同參數(shù)的同名函數(shù)。這也適用于繼承函數(shù)。以下示例展示了合約 A 中的重載函數(shù) f。

pragma solidity ^0.4.16;

contract A {

    function f(uint _in) public pure returns (uint out) {

        out = 1;

    }

    function f(uint _in, bytes32 _key) public pure returns (uint out) {

        out = 2; 

   }

}

重載函數(shù)也存在于外部接口中。如果兩個外部可見函數(shù)僅區(qū)別于 Solidity 內的類型而不是它們的外部類型則會導致錯誤。

// 以下代碼無法編譯

pragma solidity ^0.4.16;

contract A {

    function f(B _in) public pure returns (B out) {

        out = _in;

    }

    function f(address _in) public pure returns (address out) {

        out = _in;

    }

}

contract B {

}

以上兩個 f 函數(shù)重載都接受了 ABI 的地址類型,雖然它們在 Solidity 中被認為是不同的。

3.1 重載解析和參數(shù)匹配

通過將當前范圍內的函數(shù)聲明與函數(shù)調用中提供的參數(shù)相匹配,可以選擇重載函數(shù)。 如果所有參數(shù)都可以隱式地轉換為預期類型,則選擇函數(shù)作為重載候選項。如果一個候選都沒有,解析失敗。

pragma solidity ^0.4.16;

contract A {

    function f(uint8 _in) public pure returns (uint8 out) {

        out = _in;

    }

    function f(uint256 _in) public pure returns (uint256 out) {

        out = _in;

    }

}

調用 f(50) 會導致類型錯誤,因為 50 既可以被隱式轉換為 uint8 也可以被隱式轉換為 uint256。 另一方面,調用 f(256) 則會解析為 f(uint256) 重載,因為 256 不能隱式轉換為 uint8。

注解:返回參數(shù)不作為重載解析的依據(jù)。

4

繼承

通過復制包括多態(tài)的代碼,Solidity 支持多重繼承。

所有的函數(shù)調用都是虛擬的,這意味著最遠的派生函數(shù)會被調用,除非明確給出合約名稱。

當一個合約從多個合約繼承時,在區(qū)塊鏈上只有一個合約被創(chuàng)建,所有基類合約的代碼被復制到創(chuàng)建的合約中。

總的來說,Solidity 的繼承系統(tǒng)與 Python的繼承系統(tǒng) ,非常 相似,特別是多重繼承方面。

下面的例子進行了詳細的說明。

pragma solidity ^0.4.16;

contract owned {

    function owned() { owner = msg.sender;}

    address owner;

}

// 使用 is 從另一個合約派生。派生合約可以訪問所有非私有成員,包括內部函數(shù)和狀態(tài)變量,

// 但無法通過 this 來外部訪問。

contract mortal is owned {

    function kill() {

        if (msg.sender == owner) selfdestruct(owner);

    }

}

// 這些抽象合約僅用于給編譯器提供接口。

// 注意函數(shù)沒有函數(shù)體。// 如果一個合約沒有實現(xiàn)所有函數(shù),則只能用作接口。

contract Config {

    function lookup(uint id) public returns (address adr);

}

contract NameReg {

    function register(bytes32 name) public;

    function unregister() public;

 }

// 可以多重繼承。請注意,owned 也是 mortal 的基類,

// 但只有一個 owned 實例(就像 C++ 中的虛擬繼承)。

contract named is owned, mortal {

    function named(bytes32 name) {

        Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);

        NameReg(config.lookup(1)).register(name); 

   }

    // 函數(shù)可以被另一個具有相同名稱和相同數(shù)量/類型輸入的函數(shù)重載。

    // 如果重載函數(shù)有不同類型的輸出參數(shù),會導致錯誤。

    // 本地和基于消息的函數(shù)調用都會考慮這些重載。

    function kill() public {

        if (msg.sender == owner) {

            Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970); 

           NameReg(config.lookup(1)).unregister();

            // 仍然可以調用特定的重載函數(shù)。

            mortal.kill();

        }

    }

}

// 如果構造函數(shù)接受參數(shù),

// 則需要在聲明(合約的構造函數(shù))時提供,

// 或在派生合約的構造函數(shù)位置以修飾器調用風格提供(見下文)。

contract PriceFeed is owned, mortal, named("GoldFeed") {

   function updateInfo(uint newInfo) public {

      if (msg.sender == owner) info = newInfo;

   }

   function get() public view returns(uint r) { return info; }

   uint info;

}

注意,在上邊的代碼中,我們調用 mortal.kill() 來“轉發(fā)”銷毀請求。 這樣做法是有問題的,在下面的例子中可以看到:

pragma solidity ^0.4.0;

contract owned {

    function owned() public { owner = msg.sender;}

    address owner;

}

contract mortal is owned {

    function kill() public {

        if (msg.sender == owner) selfdestruct(owner);

    }

}

contract Base1 is mortal {

    function kill() public { /* 清除操作 1 */ mortal.kill(); }

}

contract Base2 is mortal {

    function kill() public { /* 清除操作 2 */ mortal.kill(); }

}

contract Final is Base1, Base2 {

}

調用 Final.kill() 時會調用最遠的派生重載函數(shù) Base2.kill,但是會繞過 Base1.kill, 主要是因為它甚至都不知道 Base1 的存在。解決這個問題的方法是使用 super:

pragma solidity ^0.4.0;

contract owned {

    function owned() public { owner = msg.sender; }

    address owner;

}

contract mortal is owned {

    function kill() public {

        if (msg.sender == owner) selfdestruct(owner);

    }

}

contract Base1 is mortal {

    function kill() public { /* 清除操作 1 */ super.kill(); }

}

contract Base2 is mortal {

    function kill() public { /* 清除操作 2 */ super.kill(); }

}

contract Final is Base1, Base2 {

}

如果 Base2 調用 super 的函數(shù),它不會簡單在其基類合約上調用該函數(shù)。 相反,它在最終的繼承關系圖譜的下一個基類合約中調用這個函數(shù),所以它會調用 Base1.kill() (注意最終的繼承序列是——從最遠派生合約開始:Final, Base2, Base1, mortal, ownerd)。 在類中使用 super 調用的實際函數(shù)在當前類的上下文中是未知的,盡管它的類型是已知的。 這與普通的虛擬方法查找類似。

4.1 基類構造函數(shù)的參數(shù)

派生合約需要提供基類構造函數(shù)需要的所有參數(shù)。這可以通過兩種方式來完成:

pragma solidity ^0.4.0;

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ù)(is Base(7))。 另一種方法是像 修飾器modifier 使用方法一樣, 作為派生合約構造函數(shù)定義頭的一部分,(Base(_y * _y))。 如果構造函數(shù)參數(shù)是常量并且定義或描述了合約的行為,使用第一種方法比較方便。 如果基類構造函數(shù)的參數(shù)依賴于派生合約,那么必須使用第二種方法。 如果像這個簡單的例子一樣,兩個地方都用到了,優(yōu)先使用 修飾器modifier 風格的參數(shù)。

4.2 多重繼承與線性化

編程語言實現(xiàn)多重繼承需要解決幾個問題。 一個問題是 鉆石問題。 Solidity 借鑒了 Python 的方式并且使用“ C3 線性化 ”強制一個由基類構成的 DAG(有向無環(huán)圖)保持一個特定的順序。 這最終反映為我們所希望的唯一化的結果,但也使某些繼承方式變?yōu)闊o效。尤其是,基類在 is 后面的順序很重要。 在下面的代碼中,Solidity 會給出“ Linearization of inheritance graph impossible ”這樣的錯誤。

// 以下代碼編譯出錯

pragma solidity ^0.4.0;

contract X {}
contract A is X {}
contract C is A, X {}

代碼編譯出錯的原因是 C 要求 X 重寫 A (因為定義的順序是 A, X ), 但是 A 本身要求重寫 X,無法解決這種沖突。

可以通過一個簡單的規(guī)則來記憶: 以從“最接近的基類”(most base-like)到“最遠的繼承”(most derived)的順序來指定所有的基類。

4.3 繼承有相同名字的不同類型成員

當繼承導致一個合約具有相同名字的函數(shù)和 修飾器modifier 時,這會被認為是一個錯誤。 當事件和 修飾器modifier 同名,或者函數(shù)和事件同名時,同樣會被認為是一個錯誤。 有一種例外情況,狀態(tài)變量的 getter 可以覆蓋一個 public 函數(shù)。

“Solidity語法的重載,繼承的定義是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關的知識可以關注億速云網(wǎng)站,小編將為大家輸出更多高質量的實用文章!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內容。

AI