创建合约时, 合约的 构造函数 (一个用关键字 constructor
声明的函数)会执行一次。 构造函数是可选的。只允许有一个构造函数,这意味着不支持重载
。
继承
我们今天不讲别的,就只讲合约继承
相关的内容。(您是不是感觉这话挺眼熟的,没错,您确实看过,前一篇开头便是今天我们不讲三国,来讲讲 流程控制
,您会觉得我上头了还是怎么!)
闲话少叙,咱们尽快的切入正题。
有过面向对象编程思想(OOP)
都知道,继承
是比较重要的一环,也正是继承
让代码复用,维护也变得简单了起来(当然咯,这只是相对而言,维护工作依旧还是那样!)。
我们知道c++
支持多继承
,而 Solidity 也是支持多继承
的,毕竟是受了它的影响嘛!从事Java
的朋友这时可能最想说的是,什么啊,Java 设计者当初就是为了避免多继承所带来的问题,而决定摒弃多继承的。
虽然说多继承
会破坏面向对象思想(OOP)
,但诸如表面上只支持单继承
的 PHP,在 PHP 5.4 却引入了triat
新特性,弥补了在 PHP 中单继承
的不足。
在 Solidity 声明继承
关系还是很容易的,使用关键字is
,例如:contract Bird is Animal {}
。
创建合约时, 合约的 构造函数 (一个用关键字 constructor
声明的函数)会执行一次。 构造函数是可选的。只允许有一个构造函数,这意味着不支持重载
。
构造函数执行完毕后,合约的最终代码将部署到区块链上。此代码包括所有公共和外部函数以及所有可以通过函数调用访问的函数。 部署的代码没有 包括构造函数代码或构造函数调用的内部函数。
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
//base contract
contract Animal {
//前面我们讲过 只要将 状态变量声明为 public,
//solidity自动会为我们生成一个与状态变量名相同的public 函数
string public name = " base Animal";
}
//contract Bird 继承了 contract Animal
contract Bird is Animal {
//构造器 初始化相关数据
constructor() {
name = "bage Bird";
}
function getName() public view returns (string memory) {
return name;
}
}
重载(Override)
刚刚我们在说到构造器
的时候,有提到重载
,对于有过面向对象思想
的您,想必是再清楚不过重载
的概念了。
而在 Solidity 实现重载
这一功能也是有相应的约束的。
virtual
: 父合约中的函数,如果希望子合约重写,需要加上virtual
关键字。override
:子合约重写了父合约中的函数,需要加上override
关键字。
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
//base contract
contract Animal {
function eat() public virtual returns(string memory){
return "animal eat";
}
}
//contract Bird 继承了 contract Animal
contract Bird is Animal {
function eat() public pure override returns(string memory) {
return "bird eat";
}
}
多重继承
展现技术的时刻到了,我们一上来就说 Solidity 支持多继承
,可你展示了这么多,也没见任何的多继承
的内容啊!
毕竟,先讲清了单继承
,多继承
也就那么回事了!
作为聪明睿智的您想必会问,多重继承
,那么怎么保证到底是哪个被继承合约
先执行呢?
您看,您这个问题,问的就比较有深度了,是的,所以在 Solidity 的多重继承
是依次排序下来的。
例如:有一个合约 A,若继承 合约 B 和 合约 C,那么应该写成contract A is B,C {}
,若合约 B 和合约 C 顺序颠倒了,那么最后执行的结果也是不一样的。
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
//base contract
contract Animal {
function eat() public virtual returns(string memory){
return "animal eat";
}
}
contract Flyer {
function fly() public virtual returns (string memory){
return " fly...";
}
}
//contract Bird 继承了 contract Animal
contract Bird is Animal,Flyer {
function eat() public pure override returns(string memory) {
return "bird eat";
}
function fly() public pure override returns (string memory){
return " bird fly...";
}
}
基类构造函数的参数
对于基合约构造函数
有参数的情况,派生合约需要指定所有参数,对于派生合约有两种方式。
- 1、派生合约在继承列表中调用基合约的构造函数。如
contract Bird is Animal("bage") {}
。 - 2、在派生合约的构造函数进行指定。如:
contract Bird is Animal { constructor() Animal("bage" ) { } }
。
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
//base contract
contract Animal {
string name;
constructor(string memory _name){
name = _name;
}
}
//第一种:派生合约继承基合约 直接在继承列表中给基类构造函数传参
contract Bird is Animal("bage") {
//其实这个写不写都没事,不写,编译器自动默认的就是这个
constructor() {}
}
contract Dog is Animal{
//第二种则在派生合约的构造函数对基合约构造函数进行传参
constructor() Animal("diba"){
}
}
如果构造函数参数是常量并且定义或描述了合约的行为,使用第一种方法比较方便。 如果基类构造函数的参数依赖于派生合约,那么必须使用第二种方法。
参数必须在两种方式中(继承列表或派生构造函数修饰符样式)使用一种 。 在这两个位置都指定参数则会发生错误。
如果派生合约没有给所有基类合约指定参数,则这个合约必须声明为抽象合约
。 在这种情况下,当另一个合约从它派生出来时,另一个合约的继承列表或构造函数必须为所有还没有指定参数的基类提供必要的参数(否则,其他合约也必须被声明为抽象的)。
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
//base contract
contract Animal {
string name;
constructor(string memory _name){
name = _name;
}
}
//爬行动物是动物
abstract contract Reptile is Animal{}
contract Snake is Reptile {
//这是一只眼镜蛇
constructor() Animal("cobra"){}
}
当继承层次结构中有多个构造函数时,继承线性化特别重要。 构造函数将始终以线性化顺序执行,无论在继承合约的构造函数中提供其参数的顺序如何。
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
//base contract
contract Animal {
string name;
constructor(string memory _name){
name = _name;
}
}
//爬行动物是动物
contract Reptile {
string public name;
constructor(string memory _name){
name = _name;
}
}
//蛇是爬行动物,又属于动物
// 构造函数以以下顺序执行:
// 1 - Reptile
// 2 - Animal
// 3 - Snake
contract Snake is Reptile,Animal {
//这是一只眼镜蛇 多个基类使用空格隔开
constructor() Reptile("pa") Animal("cobra"){}
}
// 构造函数以以下顺序执行:
// 1 - Animal
// 2 - Reptile
// 3 - Snake
contract Snake is Animal,Reptile {
//这是一只眼镜蛇 多个基类使用空格隔开
constructor() Animal("cobra") Reptile("pa") {}
}
// 构造函数仍然以以下顺序执行:
// 1 - Animal
// 2 - Reptile
// 3 - Snake
contract Snake is Animal,Reptile {
//这是一只眼镜蛇 多个基类使用空格隔开
constructor() Reptile("pa") Animal("cobra") {}
}
}