Solidity语法的重载,继承的定义是什么

发布时间:2021-12-07 15:27:48 作者:iii
来源:亿速云 阅读:163

本篇内容介绍了“Solidity语法的重载,继承的定义是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

1

摘要

以太坊智能合约语言Solitidy是一种面向对象的语言。

2

合约说明

Solidity 合约类似于面向对象语言中的类。合约中有用于数据持久化的状态变量,和可以修改状态变量的函数。 调用另一个合约实例的函数时,会执行一个 EVM 函数调用,这个操作会切换执行时的上下文,这样,前一个合约的状态变量就不能访问了。

面向对象(Object Oriented,OO)语言有3大特性:封装,继承,多态,Solidity语言也具有着3中特性。

面向对象语言3大特性的说明解释如下:

3

函数重载(Override)

合约可以具有多个不同参数的同名函数。这也适用于继承函数。以下示例展示了合约 A 中的重载函数 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; 

   }

}

重载函数也存在于外部接口中。如果两个外部可见函数仅区别于 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 函数重载都接受了 ABI 的地址类型,虽然它们在 Solidity 中被认为是不同的。

3.1 重载解析和参数匹配

通过将当前范围内的函数声明与函数调用中提供的参数相匹配,可以选择重载函数。 如果所有参数都可以隐式地转换为预期类型,则选择函数作为重载候选项。如果一个候选都没有,解析失败。

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。

注解:返回参数不作为重载解析的依据。

4

继承

通过复制包括多态的代码,Solidity 支持多重继承。

所有的函数调用都是虚拟的,这意味着最远的派生函数会被调用,除非明确给出合约名称。

当一个合约从多个合约继承时,在区块链上只有一个合约被创建,所有基类合约的代码被复制到创建的合约中。

总的来说,Solidity 的继承系统与 Python的继承系统 ,非常 相似,特别是多重继承方面。

下面的例子进行了详细的说明。

pragma solidity ^0.4.16;

contract owned {

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

    address owner;

}

// 使用 is 从另一个合约派生。派生合约可以访问所有非私有成员,包括内部函数和状态变量,

// 但无法通过 this 来外部访问。

contract mortal is owned {

    function kill() {

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

    }

}

// 这些抽象合约仅用于给编译器提供接口。

// 注意函数没有函数体。// 如果一个合约没有实现所有函数,则只能用作接口。

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

   }

    // 函数可以被另一个具有相同名称和相同数量/类型输入的函数重载。

    // 如果重载函数有不同类型的输出参数,会导致错误。

    // 本地和基于消息的函数调用都会考虑这些重载。

    function kill() public {

        if (msg.sender == owner) {

            Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970); 

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

            // 仍然可以调用特定的重载函数。

            mortal.kill();

        }

    }

}

// 如果构造函数接受参数,

// 则需要在声明(合约的构造函数)时提供,

// 或在派生合约的构造函数位置以修饰器调用风格提供(见下文)。

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() 来“转发”销毁请求。 这样做法是有问题的,在下面的例子中可以看到:

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() 时会调用最远的派生重载函数 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 的函数,它不会简单在其基类合约上调用该函数。 相反,它在最终的继承关系图谱的下一个基类合约中调用这个函数,所以它会调用 Base1.kill() (注意最终的继承序列是——从最远派生合约开始:Final, Base2, Base1, mortal, ownerd)。 在类中使用 super 调用的实际函数在当前类的上下文中是未知的,尽管它的类型是已知的。 这与普通的虚拟方法查找类似。

4.1 基类构造函数的参数

派生合约需要提供基类构造函数需要的所有参数。这可以通过两种方式来完成:

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 {

    }

}

一种方法直接在继承列表中调用基类构造函数(is Base(7))。 另一种方法是像 修饰器modifier 使用方法一样, 作为派生合约构造函数定义头的一部分,(Base(_y * _y))。 如果构造函数参数是常量并且定义或描述了合约的行为,使用第一种方法比较方便。 如果基类构造函数的参数依赖于派生合约,那么必须使用第二种方法。 如果像这个简单的例子一样,两个地方都用到了,优先使用 修饰器modifier 风格的参数。

4.2 多重继承与线性化

编程语言实现多重继承需要解决几个问题。 一个问题是 钻石问题。 Solidity 借鉴了 Python 的方式并且使用“ C3 线性化 ”强制一个由基类构成的 DAG(有向无环图)保持一个特定的顺序。 这最终反映为我们所希望的唯一化的结果,但也使某些继承方式变为无效。尤其是,基类在 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,无法解决这种冲突。

可以通过一个简单的规则来记忆: 以从“最接近的基类”(most base-like)到“最远的继承”(most derived)的顺序来指定所有的基类。

4.3 继承有相同名字的不同类型成员

当继承导致一个合约具有相同名字的函数和 修饰器modifier 时,这会被认为是一个错误。 当事件和 修饰器modifier 同名,或者函数和事件同名时,同样会被认为是一个错误。 有一种例外情况,状态变量的 getter 可以覆盖一个 public 函数。

“Solidity语法的重载,继承的定义是什么”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!

推荐阅读:
  1. solidity智能合约[36]-连续继承与多重继承
  2. PHP的重载是什么

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

solidity

上一篇:缓存一致性和跨服务器查询的数据异构解决方案是什么

下一篇:Solidity语法constant/view/pure关键字定义是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》