当前位置: 首页 > article >正文

solidity中的继承

1.继承

继承是面向对象编程很重要的组成部分,可以显著减少重复代码。如果把合约看作是对象的话,solidity也是面向对象的编程,也支持继承。 

规则

  1. virtual: 父合约中的函数,如果希望子合约重写,需要加上virtual关键字。
  2. override:子合约重写了父合约中的函数,需要加上override关键。
// SPDX-License-Identifier: MIT
// 指定了许可证类型,MIT许可证,这是一种常用的开源许可证。

pragma solidity ^0.8.22;
// 指定了Solidity编译器的版本范围,^表示与0.8.22版本兼容的最新版本。

contract Yeye{
    event Log(string msg);
    function hip() public virtual{
        emit Log("Yeye");
    }
    function pop() public virtual{
        emit Log("Yeye");
    }
    function yeye() public virtual{
        emit Log("Yeye");
    }
}
    contract Baba is Yeye{
        function hip() public virtual override{
            emit Log("Baba");
        }
        function pop() public virtual override{
            emit Log("Baba");
        }
        function baba() public virtual {
            emit Log("Baba");
        }
    }
    

在这个例子中,Baba继承了Yeye,并且重写了hip()和pop()函数,同时添加了一个新的函数baba()。override关键字用于明确指出函数重写了父合约中的虚拟函数。

2.多重继承

solidity的合约可以继承多个合约。规则:

  1. 继承时要按辈分最高到最低的顺序排。比如我们写一个Erzi合约,继承Yeye合约和Baba合约,那么就要写成contract Erzi is Yeye, Baba,而不能写成contract Erzi is Baba, Yeye,不然就会报错。
  2. 如果某一个函数在多个继承的合约里都存在,比如例子中的hip()pop(),在子合约里必须重写,不然会报错。
  3. 重写在多个父合约中重名函数时,override关键字后面要加上父合约名字,例如override(Yeye, Baba)
   contract Erzi is Yeye,Baba{
            function hip() public virtual override(Yeye,Baba){
                emit Log("Erzi");
            }
            function pop() public virtual override(Yeye,Baba){
                emit Log("Erzi");
            }
        }

在这个例子中,Erzi继承了Yeye和Baba,并且必须重写hip()和pop()函数,因为这两个函数在Yeye和Baba中都存在。

3.砖石继承(菱形继承)

菱形继承 (钻石继承) 是多重继承的一种特殊情况。

  A
 / \
B   C
 \ /
  D
contract A {
    function foo() public pure virtual returns (string memory) {
        return "A";
    }
}

contract B is A {
    function foo() public pure virtual override returns (string memory) {
        return "B";
    }
}

contract C is A {
    function foo() public pure virtual override returns (string memory) {
        return "C";
    }
}
// 根据继承顺序, B C 平级, 所以顺序随意

contract D1 is B, C {
    function foo() public pure override(B, C) returns (string memory) {
        return "D1";
    }
}

contract D2 is C, B {
    function foo() public pure override(C, B) returns (string memory) {
        return "D2";
    }
}

调用父合约的构造函数

// SPDX-License-Identifier: MIT
// 指定了许可证类型,MIT许可证,这是一种常用的开源许可证。

pragma solidity ^0.8.22;
// 指定了Solidity编译器的版本范围,^表示与0.8.22版本兼容的最新版本。

contract A{
    string public name;
    constructor(string memory _name){
        name=_name;
    }
 }
 contract B{
    uint public number;
    constructor(uint _number){
        number=_number;
    }
 }

 //方法1 在声明的继承的时候调用
 contract D is A("Hello"),B(42){}

 //方法2-在声明构造函数的时候调用
 contract C is A,B{
    constructor(string memory _name,uint _number) A(_name) B(_number){}

 }
 //方法1和2可混用
 contract E is A("Hello"), B{
    constructor(uint _number) B(_number) {}
 }
    

构造函数的调用顺序:先继承的父合约 → 后继承的父合约 → 当前合约

所以,demo 1 是 A → B → C、 demo 2 是 A → B → D、 demo 3 是 A → B → E

子合约有两种方式调用父合约的函数,直接调用和利用super关键字。

  • 直接调用:子合约可以直接用父合约名.函数名()的方式来调用父合约函数,例如Yeye.pop()
function callParent() public{
        Yeye.pop();
    }
  • super关键字:子合约可以利用super.函数名()来调用最近的父合约函数。solidity继承关系按声明时从右到左的顺序是:contract Erzi is Yeye, Baba那么Baba是最近的父合约,super.pop()将调用Baba.pop()而不是Yeye.pop()
function callParentSuper() public{
        // 将调用最近的父合约函数,Baba.pop()
        super.pop();
    }

4.总结

讲解一下完整的使用方法:

// SPDX-License-Identifier: MIT
// 这是一个许可证标识符,用于指定代码的许可证类型。MIT许可证是一种宽松的自由软件许可证。

pragma solidity ^0.8.26;
// 这行代码指定了Solidity编译器的版本。`^`符号表示与0.8.26版本兼容的最新版本。

/* Inheritance tree
A / \
B C \
 / D
*/
// 这是一个继承树的注释,说明了合约之间的继承关系。A是基类,B和C继承自A,D继承自B和C。

contract A {
    // 定义了一个名为A的合约。

    // 这是一个事件。你可以在函数中触发事件,并将它们记录在交易日志中。
    // 在我们的例子中,这将有助于追踪函数调用。
    event Log(string message);
    // 事件Log被定义,它接受一个字符串参数message。

    function foo() public virtual {
        emit Log("A.foo called");
    }
    // 函数foo被定义为public,并且被标记为virtual,这意味着它可以被重写。
    // 当foo被调用时,它触发了事件Log,并传递了字符串"A.foo called"。

    function bar() public virtual {
        emit Log("A.bar called");
    }
    // 函数bar也被定义为public和virtual,当bar被调用时,它触发了事件Log,并传递了字符串"A.bar called"。
}

contract B is A {
    // 定义了一个名为B的合约,它继承自合约A。

    function foo() public virtual override {
        emit Log("B.foo called");
        A.foo();
    }
    // 函数foo在B中被重写。它触发了事件Log,表示"B.foo called",然后调用了父合约A中的foo函数。

    function bar() public virtual override {
        emit Log("B.bar called");
        super.bar();
    }
    // 函数bar在B中被重写。它触发了事件Log,表示"B.bar called",然后使用super关键字调用了父合约A中的bar函数。
}

contract C is A {
    // 定义了一个名为C的合约,它继承自合约A。

    function foo() public virtual override {
        emit Log("C.foo called");
        A.foo();
    }
    // 函数foo在C中被重写。它触发了事件Log,表示"C.foo called",然后调用了父合约A中的foo函数。

    function bar() public virtual override {
        emit Log("C.bar called");
        super.bar();
    }
    // 函数bar在C中被重写。它触发了事件Log,表示"C.bar called",然后使用super关键字调用了父合约A中的bar函数。
}

contract D is B, C {
    // 定义了一个名为D的合约,它同时继承自合约B和C。

    // Try:
    // - Call D.foo and check the transaction logs.
    //   Although D inherits A, B and C, it only called C and then A.
    // - Call D.bar and check the transaction logs
    //   D called C, then B, and finally A.
    //   Although super was called twice (by B and C) it only called A once.

    function foo() public override(B, C) {
        super.foo();
    }
    // 函数foo在D中被重写。它使用super关键字调用了父合约中的foo函数。
    // 由于D继承自B和C,而B和C都重写了foo函数,这里的super.foo()将调用C中的foo函数,然后C中的foo会调用A中的foo。

    function bar() public override(B, C) {
        super.bar();
    }
    // 函数bar在D中被重写。它使用super关键字调用了父合约中的bar函数。
    // 由于D继承自B和C,而B和C都重写了bar函数,这里的super.bar()将首先调用C中的bar函数,然后C中的bar会调用B中的bar,最后B中的bar会调用A中的bar。
}


http://www.kler.cn/a/377119.html

相关文章:

  • 一种基于动态部分重构的FPGA自修复控制器
  • 【阻塞队列】- ArrayBlockingQueue 的原理-迭代器
  • 有什么AI辅助阅读文献工具推荐?
  • 如何在 Vue 2 中使用 Swiper 5.4.5 处理静态与后端数据不能切换问题
  • C语言优化技巧--达夫设备(Duff‘s Device)解析
  • Ubuntu 22.04 升级 24.04 问题记录
  • 质数的小游戏~(牛客,cf)
  • 《机器人SLAM导航核心技术与实战》第1季:第10章_其他SLAM系统
  • 【VUE+DRF】案例升级
  • 如何在Oracle数据库中获取版本信息
  • es拼音分词器(仅供自己参考)
  • 前端react常见面试题目(basic)
  • 树莓派开发相关知识七 -串口数码管
  • 从0开始学统计-什么是中心极限定理
  • [perl] 数组与哈希
  • 【Linux】IPC进程间通信:并发编程实战指南(一)
  • 纯前端生成PDF(jsPDF)并下载保存或上传到OSS
  • 提升当当网数据爬取效率:代理IP并发抓取技术
  • [Redis] Redis事务
  • Linux内核与用户空间
  • 问:Redis如何做到原子性?
  • (自用)机器学习python代码相关笔记
  • ES入门:查询和聚合
  • Java之继承
  • Rust 跨平台应用的最佳实践
  • MiniWord