原文链接:以太坊智能合约(九):Solidity继承
本文主要讲解了有关智能合约继承的概念、继承的参数传递、重写函数以及Solidity的继承中的调用关系与多继承。掌握区块链技术,学习智能合约。
一、目录
☞继承的概念
☞继承的参数传递
☞重写函数
☞Solidity的继承中的调用关系与多继承
二、继承的概念
Solidity语言为我们提供了继承的支持,实现的方式是通过复制包括多态的代码到子类来实现的。
继承通过关键字is来实现,一起来看看下面的例子:
上面的例子中,合约Manager继承了Person合约。
继承的合约Manager可以访问所有的非私有成员。包括internal的函数和状态变量(这些是不能通过external的方式访问的,如this.yourFunc()),我们来看下面的例子:
从上面的例子中,我们可以看到,子类可以访问父类的public,internal权限控制变量或函数,不能访问private权限控制的变量和函数。在子类中可以直接访问状态变量,原因是因为状态变量默认是internal的。
三、继承的参数传递
继承支持传参,继承时可以有两种方式传参数到父类。下面来看第一种方式:
另外一种方式是类似修改器的语法,来直接看一个例子:
如果要传入到基类的是简单的常量,第一种方式会更加简洁。但如果传入的参数与子类的输入参数有关,那么你应该使用第二种方式,以获取参数值。如果你同时使用了这两种方式,后一种方式将最终生效。
四、重写函数
在子类中允许重写函数,但不允许重写返回参数的类型,一起来看看下面的代码:
上面代码中的function data() returns(string){}将导致Override changes extended function signature报错,因为不能修改返回的参数类型。
五、Solidity的继承中的调用关系与多继承
在继承链中,由于继承实现是代码复制。如果出现函数重写(类似其它语言的函数重载),最终使用的是继承链上哪个合约定义的代码呢?实际执行时,依据的是最远继承的原则(most derived)。下面来看一个例子:
上面的例子中,根据最远继承原则,大家可以想想MostDerived1,和MostDerived2中调用call()方法的运行结果。
实际上呢,MostDerived1,MostDerived2的call()将分别返回2和1。因为对于MostDerived1的最远继承合约是Base2,所以会使用其对应的函数,对于MostDerived2最远的继承者将是Base1。
5.1 指定调用父合约方法
虽然存在最远继承原则,但是我们仍可以在子合约中主动调用父合约被重写的方法来触发被覆盖的函数。下面来看一个清理的例子:
因为三个合约的继承关系是:SpecifyBase <= mortal <= owned,所以SpecifyBase中可以直接调用父级mortal的kill。如果不指定那么SpecifyBase中的kill仅相当于重写父级的kill函数。在SpecifyBase我们打算完成一些特有的清理流程,但想复用父合约的清理流程,可以通过mortal.kill()的方式直接调用父合约的函数。
5.2 super关键字
当我们在进行一些清理的时候,有些时候,我们希望继承链条上每一个函数都能被调用以进行一些清理工作,这个时候,我们需要用到super关键字,继续来看一个更多复杂的清理流程的代码:
上面的例子中,在Final中调用kill(),将仅仅触发Base2.kill()被调用,因为它是最远继承合约,从而跳过Base1.kill()。如果我们想也触发Base1.kill(),解决方案是使用super。
在上面的代码中,我们调用FinalWithSuper的kill()方法,将触发按最远继承原则形成的链 Final,Base2,Base1,motal,owned的调用,上述代码运行的事件如下:
这样我们就实现了对链条上的所有方法的调用。另外,不知大家是否已经注意到,在使用super的上下文中,实际并不知道最终的调用链是如何的,还与继承的关系有关。
5.3 多继承与线性化
在Solidity中,允许多继承,你可以同时继承多个合约。实现多继承的编程语言需要解决几个问题,其中之一是菱形继承问题又称钻石问题(假设我们有类B和类C,它们都继承了相同的类A。另外我们还有类D,类D通过多重继承机制继承了类B和类C,因此D从理论上来讲有两份A的属性和方法。因为上述图表的形状类似于钻石(或者菱形),因此这个问题被形象地称为钻石问题(菱形继承问题))。如下图。
Solidity的解决方案是根据is后面的合约顺序来线性化继承关系,即overwrite的顺序可以是,D < B < C < A, 也可以是 D < C < B < A。因此在D里面只会有一个同名属性或者函数的副本,而不会出现菱形问题的两个副本并存的情况。 下面的代码,Solidity会报错Linearization of inheritance graph impossible。
原因是C会请求X来重写A(因为继承定义的顺序是A,X),但A自身又是重写X的,所以这是一个不可解决的矛盾。
一个简单解决这种矛盾的原则是,总是指定基合约的继承顺序是从most base-like到most derived。总之, 按照既定的顺序继承就不会有问题, 先继承祖父的再继承父亲。
-END-