暂时的一事无成也代表将来万事皆有可能!
目录
一、对Solidity文件的理解
二、Solidity的导sol文件(库、合约)
三、Solidity的继承
1.继承的分类
2.继承的可见性
3.父合约构造函数的传参
4.调用父合约成员
5.重写
四、Solidity的抽象合约
五、Solidity接口合约
实现接口
六、Solidity的库
库合约的存在形式
七、Solidity的对象
一、对Solidity文件的理解
Solidity文件是Solidity语言文件,里面包含Solidity语言代码,用于编写合约,文件名以.sol结尾。
一个sol文件可以创建(含有)许多的contract(合约),不单单只有一个
那同一个sol文件中的各个合约如何实现交互呢?
不同的sol文件的各个合约又如何进行交互呢?
答案是:继承和对象以及导文件导库
二、Solidity的导sol文件(库、合约)
关键字:import
格式:import sol文件名.sol (文件的相对位置)
可以进行导入外部sol文件,导的sol文件和本身的sol文件相当于变成同一个sol合约
具体操作
import关键字使用:导入其他源文件
1. imporot "solidity文件名" :导入该文件全局作用域到当前全局作用域中 2.import * as 别名 "solidity文件名" :
2.import "solidity文件名" as 别名 :导入该文件全局作用域到当前全局作用域中,并起一个文件别名,通过别名可创建导入文件下某一合约的对象,经常用
3.imoport {合约名 as 别名,合约名} from "solidity文件夹名" :导入导入文件夹名
下的某几个合约到该文件中
三、Solidity的继承
像java类的继承一样,在solidity中,那就是合约的继承
Solidity 语言是一种面向对象的编程语言,提供了对合约继承的支持,继承是扩展合约功能的一种方式。
Solidity 语言的合约继承通过关键字 is 来完成继承。
继承的关键字:is
继承的本质:继承的实现方案是代码拷贝,所以合约继承后,部署到网络时,将变成一个合约,代码从父类拷贝到子类中。
被继承的合约我们称之为父合约(基类合约),继承了的合约称之为子合约(派生合约)
Solidity中合约继承的重要特点
- 派生合约可以访问父合约所有非私有private的成员,包括内部方法和状态变量。但是不允许使用this(代表需要使用创建对象的形式来访问external的东西)
- 如果函数名保持不变,则允许函数重写,如果派生合约与父合约函数对应出参和入参类型和个数不同,则将编译将失败(重写的本质是改变函数体)
- 使用super关键字或父合约名调用父合约函数,或者直接调用
- 在多重继承的情况下,使用super的父合约函数调用,优先选择被最多继承的合约
1.继承的分类
单继承:
一个合约继承一个合约
例如:
contract X {}
contract A is X{}
多继承
一个合约被多个合约继承
例如
contract X {}
contract A is X{}
contract B is X{}
多层继承
但可以有爷爷,祖先,而这就是多重继承
合约被合约继承,继承的合约又被另一个合约继承
注意事项:
当多重继承合约时,这些父合约中不允许出现相同的状态变量名
当多重继承合约时,这些父合约中允许出现相同的函数名,事件名,修改器名
例如
contract X {}
contract A is X{}
contract B is A{}
多重继承示例
Solidity 语言提供了对合约继承的支持,而且支持多重继承。
Solidity 语言的多重继承采用线性继承方式。继承顺序很重要,判断顺序的一个简单规则是按照“最类似基类”到“最多派生”的顺序指定基类。
第一种情况:基类 X,Y 没有继承关系,派生类 Z 继承了 X,Y。
X Y / \ \ / Z多重继承格式
子合约 is 父合约1,父合约2····{
}
例
contract X {}
contract A {}
contract B is X,A{}
2.继承的可见性
子合约不能访问父合约的private修饰的私有成员
子合约可以直接访问访问父合约的所有非私有(public、internal)成员
子合约不能直接访问父合约的external修饰的外部成员,因为子合约不能对父合约的东西使用this,想要访问到就必须创建父合约对象的方式来调用间接访问
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract A{
uint stateVar;//状态变量默认是internal权限
function somePublicFun() public{}
function someInternalFun() internal{}
function somePrivateFun() private{}
function someExternalFun() external{}
}
contract B is A{
function call() public {
//访问父类的`public`方法
somePublicFun();
//访问父类的状态变量(状态变量默认是internal权限)
stateVar = 10;
//访问父类的`internal`方法
someInternalFun();
//不能访问`private`
//somePrivateFun();
A a = new A();
a.someExternalFun();
}
}
3.父合约构造函数的传参
派生的合约初始化前需要调用所有父合约的构造函数,并且还需要提供所有父合约所需的参数
提供父合约构造函数需要的所有参数,有两种提供方式
第一种称
在声明继承的时候
派生合约名 is 父合约名(参数) { }
此方式对于构造函数是常量情况的时候比较方便,可以直接说明合约的行为
第二种称
在派生合约的构造器中
constructor(参数列表) 父合约名(子合约定义好的参数列表){
}
注意:如果是多重继承,那么同理
派生合约名 is 父合约名(参数),·····{ }
constructor(参数列表) 父合约名(子合约定义好的参数列表),·······{
}
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract test {
uint a=1;
constructor(uint aa) {
a=aa;
}
}
contract test1 is test(12) {
}
contract test2 is test {
constructor(uint v) test(v){
}
}
4.重写
允许重写:
函数
不允许重写:
状态变量
在子合约中允许重写函数,但不允许重写函数入参和出参,即参数类型和个数不能修改
重写步骤:
1.父合约要被重写的函数必须加上能被重写修饰符 virtual
2.子合约重写的函数上面必须加上重写修饰符 override
温馨提示:当父合约里面有两个以上同名方法的时候,子合约必须重写夫合约里面的所有同名方法,格式:override(父合约名,······)
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
contract test {
uint a=1;
function set() virtual public {
a=2;
}
}
contract test1 {
function set() virtual public {
}
}
contract test2 is test,test1 {
function set() public override(test,test1){
super.set();
}
}
温馨提示:
midifier函数修改器(后面讲)也会随着父合约被继承从而被继承,被继承后,在子合约中我们还可以对父合约中的修改器进行重写和使用
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Person{
string public name;
uint public age;
function getSalary() external pure virtual returns(uint){
return "unkown";
}
}
contract Employee is Person{
function getSalary() external pure override returns(uint){
return 3000;
}
}
5.调用父合约成员
当一个合约从多个合约继承时,在区块链上只有一个合约被创建,所有基类合约的代码被编译到创建的合约中。这意味着对基类合约函数的所有内部调用也只是使用内部函数调用
1.直接调用和使用(适用于单继承,或者其他继承但名不冲突的情况下)
2. 利用父合约名(这样更好,适用多种类型的继承)
3.super关键字 (使用super可以访问父合约的函数,但不能访问状态变量,如果是多层和多重继承,那么它将调用所有的父类的该名的方法)
使用super调用
使用super调用,格式为:super.<method>。
单继承:
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract X { function foo() internal pure returns(uint) { return 1; } } contract Z is X { function test() external pure returns(uint) { // supper 表示父级合约,foo为父级合约的方法名称 uint result = supper.foo(); return result; } }多层、多重继承:
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract X { event log(string message); function foo() public virtual { emit log("X.foo"); } } contract Y is X { function foo() public virtual override { emit log("Y.foo"); super.foo(); } } contract Z is X { function foo() public virtual override { emit log("Z.foo"); super.foo(); } } contract T is Y,Z { function foo() public override(Y,Z) { super.foo(); } }
四、Solidity的抽象合约
抽象合约和普通合约一样,只不过抽象合约里面可以存在未实现的函数(function 函数名() 函数修饰符 ; ),且子类合约必须重写未实现的函数,可以说,如果一个普通合约里面有一个未实现的函数,那么编译会出错,除非加上abstract。或者普通合约必须要求子类做一件事(必须重写某个函数)可以使用抽象合约
关键字: abstract
使用格式:
abstract contract 抽象合约名 {
状态变量;
未实现的函数;
·················
}
抽象合约的作用:
抽象合约 abstract 的作用是将函数定义和具体实现分离,从而实现解耦、可拓展性,其使用规则为
简单来说:抽象合约定义后,是专门用来继承用的,它相比于其他的继承做了一种强迫的事情,就是子合约必须重写抽象合约里面的所有函数
abstract还可以避免子合约不对父合约进行构造函数传参的问题
注意事项
- 当合约中有未实现的函数时,则合约必须修饰为abstract;
- 当合约继承的基合约中有构造函数,但是当前合约并没有对其进行传参时,则必须修饰为abstract;
- abstract合约中未实现的函数必须在子合约中实现
- abstract合约不能单独部署,必须被继承后才能部署;
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.9.0;
abstract contract Animal {
string public species;
constructor(string memory _base) {
species = _base;
}
}
abstract contract Feline {
uint public num;
function utterance() public pure virtual returns (bytes32);
function base(uint _num) public returns(uint, string memory) {
num = _num;
return (num, "hello world!");
}
}
// 由于Animal中的构造函数没有进行初始化,所以必须修饰为abstract
abstract contract Cat1 is Feline, Animal {
function utterance() public pure override returns (bytes32) { return "miaow"; }
}
contract Cat2 is Feline, Animal("Animal") {
function utterance() public pure override returns (bytes32) { return "miaow"; }
}
五、Solidity接口合约
接口本意是物体之间连接的部位。例如:电脑的 usb 接口可以用来连接鼠标也可以连接U盘和硬盘。因此,使用标准的接口可以极大的拓展程序的功能。在 solidity 语言中,接口可以用来接受相同规则的合约,实现可更新的智能合约。
interface 类似于抽象合约,但它们不能实现任何功能。还有其他限制:
- 不能继承其他合约或者接口(是接口不能去继承接口和合约,表示它不能被继承)
- 不能定义构造器(构造函数)
- 不能定义状态变量(变量、结构体、数组、映射、枚举等)
- 只能定义普通函数和修改函数
- 所有声明的函数必须是 external 的。
- 接口里面的函数必须全部实现
接口的特点:定义函数的时候它不需要写virtual,且函数修饰符都必须写external
接口关键字:interface
定义格式:
interface 接口合约名 {
定义接口函数1;
·········;
定义接口函数n;
}
实现接口
实现接口和继承一样,使用关键字is
实现接口函数用override修饰符
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
interface Bank{
function outputMoney(uint _money) external ;
function intputMoney(uint _money) external ;
}
contract own is Bank {
uint public money;
function outputMoney(uint _money) external override {
money+=_money;
}
function intputMoney(uint _money) external override {
money-=_money;
}
}
六、Solidity的库
Solidity 智能合约中通用的代码可以提取到库 library,以提高代码的复用性和可维护性。
库 library 是智能合约的精简版,就像智能合约一样,位于区块链上,包含可以被其他合约使用的代码。
库合约内部一般定义成public和internal权限,定义成 external 毫无意义,因为库合约函数只在内部使用,不独立运行。同样,定义成 private 也不行,因为其它合约无法使用。
对比普通合约来说,库有以下限制
- 无状态变量
- 不能继承和被继承
- 不能接收以太币
- 不能销毁一个库
代表只能有函数
关键字:library
格式:
library 库名{
库方法
·····
}
使用库 library 的合约,可以将库合约视为隐式的父合约,当然它们不会显式的出现在继承关系中。也就是不用写 is 来继承,直接可以在合约中使用。
库的使用
如果库和使用库的合约在同一个sol文件,那么即可直接用
如果库的使用库的合约不在同一个sol文件,那么需要先导库import
第一种使用方式
库名.库合约函数(参数值1,参数值2····) 的方式直接调用库里面的函数
第二种使用方式
关键字:using ... for .....
再使用using 库合约名 for 数据类型
使用 using for 语法附着的数据类型,在使用的时候,可以直接用 <variable>.<method> 的形式调用,而且省略代表自己的第一个参数。
格式:
using 库名 for 使用的类型(using 库名 for * 代表附着到全部类型)
然后使用
格式: 使用的类型数据.库合约内函数名(参数值2,``````) //该变量默认为第一个参数
温馨提示:using ... for... 的使用必须写在函数外,让其变成状态变量,来对该类型使用
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
library ss{
function Math(uint _a,uint _b) public pure returns(uint ) {
if(_a==0 || _b==0){
return 0;
}else{
return (_a*_b);
}
}
}
contract gg {
uint a=1;
uint b=0;
function js() public view returns(uint){
return ss.Math(a,b);
}
using ss for uint;
function jss() public view returns(uint){
return a.Math(b);
}
}
库合约的存在形式
库 library 有两种存在形式:
- 内嵌(embedded):当库中所有的方法都是internal时,此时会将库代码内嵌在调用合约中,不会单独部署库合约;
- 链接(linked):当库中含有external或public方法时,此时会单独将库合约部署,并在调用合约部署时链接link到库合约。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
library Math {
function find(uint[] storage arr, uint val) internal view returns(uint){
for (uint i=0; i<arr.length; i++) {
if (arr[i] == val) {
return i;
}
}
revert("not found");
}
}
contract MathTest {
//直接使用
uint[] a;
uint b;
function tests() public view {
Math.find(a,b);
}
//using for使用
// 将 libray Math 附着到类型 uint[]
using Math for uint[];
uint[] arr = [1,2,3];
function test() external view returns(uint){
return arr.find(2);
}
}
七、Solidity的对象
Solidity的对象可以帮助我们访问到非继承合约里面的函数,但不能访问到里面的状态变量,它好比于是两个合约之间的媒人
对象的创建方式
合约名 对象名 = new 合约名()
这就完成了该合约对象的创建,创建完该对象后,那么创建该对象的合约就可以使用这个对象去调用对象对应合约的函数
常用于
子合约访问父合约external的状态变量和函数
使用非继承关系合约的函数
具体操作
1. solidty文件夹名 对象名= new solidity文件夹名()
2.文件别名 对象名=new 文件别名()
3.文别名.合约名 对象名 =new 文别名.合约名()
4. 合约别名 对象名 =new 合约别名()
5. 合约名 对象名 =new 合约名()文章来源:https://www.toymoban.com/news/detail-473813.html
当然也可以不起别名,那就用solidity文件夹名,或合名文章来源地址https://www.toymoban.com/news/detail-473813.html
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Math {
int[2] arr;
function aa() public view returns(int[2] memory){
return arr;
}
}
contract MathTest {
//创建Math合约对象
Math m = new Math();
function get() public view returns(int[2] memory){
//调用Math合约的东西
return m.aa();
}
}
到了这里,关于Solidity基础五的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!