哪里公司建设网站好,培训美工设计师,网站建设原则,嘉兴企业网站模板提示#xff1a;文章写完后#xff0c;目录可以自动生成#xff0c;如何生成可参考右边的帮助文档 文章目录1. 为什么需要继承1.1 代码复用的问题1.2 继承的解决方案1.3 继承的实际应用场景2. 单继承2.1 单继承基础语法2.2 访问权限3. 多重继承3.1 多重继承基础3.2 C3线性化…提示文章写完后目录可以自动生成如何生成可参考右边的帮助文档文章目录1. 为什么需要继承1.1 代码复用的问题1.2 继承的解决方案1.3 继承的实际应用场景2. 单继承2.1 单继承基础语法2.2 访问权限3. 多重继承3.1 多重继承基础3.2 C3线性化算法4. super关键字4.1 super的作用4.2 单继承中的super4.3 多重继承中的super5. 构造函数继承5.1 构造函数执行顺序5.2 构造函数参数传递5.3 多重继承的构造函数6. 函数重写6.1 virtual和override关键字6.2 函数签名必须匹配6.3 多重继承中的override6.4 使用super在重写中调用父函数7. 抽象合约7.1 什么是抽象合约8. 接口8.1 什么是接口8.3 ERC20接口标准8.4 接口用于合约交互1. 为什么需要继承1.1 代码复用的问题在没有继承机制的情况下开发者会遇到严重的代码复用问题。问题场景假设你要创建三个代币合约稳定币、治理代币、奖励代币。它们都需要ERC20基本功能transfer、approve等权限控制只有owner可以执行某些操作暂停功能紧急情况下暂停转账没有继承的做法// 稳定币合约 contract StableCoin{// 复制粘贴ERC20代码 mapping(addressuint256)public balanceOf;functiontransfer(address to, uint256 amount)public{}// 复制粘贴权限控制代码 address public owner;modifieronlyOwner(){}// 复制粘贴暂停功能代码 bool public paused;modifierwhenNotPaused(){}functionpause()public{}}// 治理代币合约 contract GovernanceToken{// 又复制粘贴一遍所有代码... mapping(addressuint256)public balanceOf;functiontransfer(address to, uint256 amount)public{}address public owner;//... 完全重复的代码}// 奖励代币合约 contract RewardToken{// 再次复制粘贴...}这种做法的问题代码冗余三个合约有90%的代码相同浪费存储空间增加部署成本维护困难发现bug需要修改三个地方容易遗漏一致性难以保证升级麻烦添加新功能需要修改所有合约无法批量更新测试成本高容易出错复制粘贴可能出错某个合约可能用旧版本代码难以追踪哪个版本最新1.2 继承的解决方案继承Inheritance是面向对象编程的核心特性它允许一个合约子合约继承另一个合约父合约的属性和方法。使用继承的做法// 基础合约1ERC20功能 contract BaseERC20{mapping(addressuint256)public balanceOf;functiontransfer(address to, uint256 amount)public virtual returns(bool){require(balanceOf[msg.sender]amount);balanceOf[msg.sender]-amount;balanceOf[to]amount;returntrue;}}// 基础合约2权限控制 contract Ownable{address public owner;constructor(){ownermsg.sender;}modifieronlyOwner(){require(msg.senderowner,Not owner);_;}}// 基础合约3暂停功能 contract Pausable{bool public paused;modifierwhenNotPaused(){require(!paused,Paused);_;}functionpause()internal{pausedtrue;}}// 子合约继承所有功能 contract StableCoin is BaseERC20, Ownable, Pausable{// 自动获得所有父合约的功能 // 只需要添加特有功能functionemergencyPause()public onlyOwner{pause();}}contract GovernanceToken is BaseERC20, Ownable, Pausable{// 同样继承所有功能}contract RewardToken is BaseERC20, Ownable, Pausable{// 同样继承所有功能}继承的优势代码复用公共功能只写一次多个子合约共享减少90%以上的重复代码易于维护bug修复只改一处所有子合约自动受益保证一致性模块化设计功能分离清晰每个合约职责单一易于理解和测试灵活扩展子合约可以添加新功能可以重写父合约函数组合不同功能模块1.3 继承的实际应用场景场景1代币项目// 基础代币 → 标准代币 → 项目代币ERC20 → ERC20Burnable → MyToken场景2权限管理// 基础权限 → 角色管理 → 具体合约Ownable → AccessControl → ProjectContract场景3安全功能// 基础合约 → 安全增强 → 最终合约BaseContract → ReentrancyGuard Pausable → SecureContract场景4可升级合约// 存储合约 → 逻辑合约 → 代理合约Storage → Logic → Proxy2. 单继承2.1 单继承基础语法单继承是最简单的继承形式一个子合约只继承一个父合约。基本语法contract Parent{// 父合约代码}contract Child is Parent{// 子合约代码 // 自动继承Parent的所有内容}关键字说明is表示继承关系Child子合约派生合约Parent父合约基础合约简单示例// SPDX-License-Identifier: MIT pragma solidity ^0.8.19;contract Parent{uint256 public value;functiongetValue()public view returns(uint256){returnvalue;}functionsetValue(uint256 _value)public{value_value;}}contract Child is Parent{// 自动继承 // - value状态变量 // - getValue()函数 // - setValue()函数 // 添加新功能functiondoubleValue()public view returns(uint256){returnvalue *2;// 可以直接访问父合约的value}}子合约获得了什么// 部署Child合约后可以调用 Child childnew Child();child.value();// 继承自Parent child.getValue();// 继承自Parent child.setValue(100);// 继承自Parent child.doubleValue();// Child自己的函数2.2 访问权限子合约对父合约的访问权限取决于可见性修饰符。public最开放子合约和外部都能访问internal只有子合约能访问外部不能private最严格连子合约都不能访问3. 多重继承3.1 多重继承基础Solidity支持多重继承一个合约可以同时继承多个父合约。基本语法contract Child is Parent1, Parent2, Parent3{// 同时继承多个父合约}继承顺序从左到右列出父合约顺序很重要影响super调用和函数解析基础示例contract Parent1{functionfoo()public virtual returns(string memory){returnParent1;}}contract Parent2{functionbar()public virtual returns(string memory){returnParent2;}}// 多重继承 contract Child is Parent1, Parent2{// 自动获得foo()和bar()functiontest()public view returns(string memory, string memory){return(foo(), bar());}}3.2 C3线性化算法Solidity使用C3线性化算法C3 Linearization确定继承顺序。基本规则从左到右Child is A, B, CA最先C最后深度优先先处理父合约的父合约保持单调性不能打乱已有的顺序简单示例继承声明contract C is A, B继承顺序C → A → B调用链C.foo()→ A.foo()→ B.foo()4. super关键字4.1 super的作用super关键字用于调用父合约的函数即使子合约已经重写了该函数。基本概念contract Parent{functiongreet()public virtual returns(string memory){returnHello from Parent;}}contract Child is Parent{functiongreet()public override returns(string memory){// 调用父合约的greet()string memory parentGreetingsuper.greet();returnstring.concat(Hello from Child, , parentGreeting);}}调用过程Child.greet()↓获取super.greet()的返回值“Hello from Parent”↓拼接“Hello from Child, Hello from Parent”↓返回最终结果4.2 单继承中的super在单继承中super指向唯一的父合约。contract Counter{uint256 public count;functionincrement()public virtual{count1;}}contract DoubleCounter is Counter{functionincrement()public override{// 先调用父合约的increment1 super.increment();// 再自己加一次再1 count1;// 最终效果每次2}}4.3 多重继承中的super在多重继承中super不是指向单个父合约而是按照继承顺序调用下一个合约。关键理解contract A{functionfoo()public virtual returns(string memory){returnA;}}contract B is A{functionfoo()public virtual override returns(string memory){returnstring.concat(B-, super.foo());}}contract C is A{functionfoo()public virtual override returns(string memory){returnstring.concat(C-, super.foo());}}contract D is B, C{functionfoo()public override(B, C)returns(string memory){returnstring.concat(D-, super.foo());}}调用链分析1、Solidity 从右到左处理继承列表。2、C 在继承列表中更靠右所以在 D 之后首先出现。3、B 在 C 之后。4、A 是共同的基类放在最后。D.foo()执行 ↓ super.foo()→ 指向 C线性化顺序的下一个 ↓ C.foo()执行 ↓ C 中的 super.foo()→ 指向 B不是 A ↓ B.foo()执行 ↓ B 中的 super.foo()→ 指向 A ↓ A.foo()返回A↓ B.foo()返回B-A↓ C.foo()返回C-B-A↓ D.foo()返回D-C-B-A重要提示 super 的含义在多重继承中super 不是指直接父合约而是指C3 线性化顺序中的下一个合约。 在 D 的上下文中C 虽然直接继承自 A但 C 的 super 会指向 B 这是因为在整个继承链中C 的下一个是 B而不是直接跳到 A5. 构造函数继承5.1 构造函数执行顺序构造函数总是按照继承顺序执行从父到子。执行规则父合约优先所有父合约构造函数先执行按继承顺序从左到右最后是子合约子合约构造函数最后执行示例contract A{uint256 public valueA;constructor(){valueA1;// 第1个执行}}contract B{uint256 public valueB;constructor(){valueB2;// 第2个执行}}contract C is A, B{uint256 public valueC;constructor(){valueC3;// 第3个执行}}执行顺序部署C合约↓A的构造函数执行valueA 1↓B的构造函数执行valueB 2↓C的构造函数执行valueC 3↓完成5.2 构造函数参数传递当父合约的构造函数需要参数时有两种传递方式。方式1在继承声明时传递固定值contract Parent{uint256 public value;constructor(uint256 _value){value_value;}}// 在继承声明时传递固定值 contract Child is Parent(100){constructor(){// Parent构造函数接收100}}特点适合固定值代码简洁不够灵活方式2在子构造函数中传递动态值contract Parent{uint256 public value;constructor(uint256 _value){value_value;}}// 在子构造函数中传递动态值 contract Child is Parent{constructor(uint256 _value)Parent(_value){// 通过参数传递给Parent}}特点更灵活可以传递动态值推荐使用5.3 多重继承的构造函数多个父合约都需要参数时必须全部初始化。contract A{uint256 public valueA;constructor(uint256 _a){valueA_a;}}contract B{uint256 public valueB;constructor(uint256 _b){valueB_b;}}contract C is A, B{uint256 public valueC;// 方式1混合传递 constructor(uint256 _a, uint256 _c)A(_a)// 动态传递给A B(200)// 固定值传递给B{valueC_c;}// 方式2全部动态传递推荐 constructor(uint256 _a, uint256 _b, uint256 _c)A(_a)B(_b){valueC_c;}}执行顺序保持不变无论如何传递参数执行顺序始终是A() → B() → C()6. 函数重写6.1 virtual和override关键字函数重写Function Overriding允许子合约修改父合约函数的行为。基本规则父合约函数必须标记为virtual子合约重写函数必须标记为override两者必须配对缺一不可基础示例contract Parent{// virtual表示这个函数可以被重写functiongetValue()public virtual returns(uint256){return100;}}contract Child is Parent{// override表示重写父合约的函数functiongetValue()public override returns(uint256){return200;// 修改返回值}}6.2 函数签名必须匹配重写函数必须与父合约函数签名完全一致。必须相同的部分函数名参数类型参数顺序返回类型可以不同的部分可见性可以更开放不能更严格状态修饰符可以更严格不能更宽松示例contract Parent{functionfoo(uint256 a)public virtual returns(uint256){returna;}}contract Child is Parent{// 正确签名完全相同functionfoo(uint256 a)public override returns(uint256){returna *2;}// 错误参数不同 //functionfoo(uint256 a, uint256 b)public override returns(uint256){//returna b;//}// 错误返回类型不同 //functionfoo(uint256 a)public override returns(string memory){//returnhello;//}// 正确internal → public更开放functionfoo()public override returns(uint256){return2;}// 错误internal → private更严格 //functionfoo()private override returns(uint256){//return2;//}}6.3 多重继承中的override当多个父合约有同名函数时必须明确指定重写哪些。contract A{functionfoo()public virtual returns(string memory){returnA;}}contract B{functionfoo()public virtual returns(string memory){returnB;}}contract C is A, B{// 必须明确指定override(A, B)functionfoo()public override(A, B)returns(string memory){returnC;}// 错误不明确 //functionfoo()public override returns(string memory){//returnC;//}}语法规则// 单继承简单overridefunctionfoo()public override returns(string memory){}// 多重继承明确指定functionfoo()public override(Parent1, Parent2)returns(string memory){}// 如果继续被继承还要加virtualfunctionfoo()public virtual override(Parent1, Parent2)returns(string memory){}6.4 使用super在重写中调用父函数contract Logger{event Log(string message);functionlog(string memory message)public virtual{emit Log(message);}}contract TimestampLogger is Logger{functionlog(string memory message)public override{// 先调用父合约的log super.log(message);// 再添加时间戳日志 emit Log(string.concat(Timestamp: , uint2str(block.timestamp)));}functionuint2str(uint _i)internal pure returns(string memory){if(_i0)return0;uint j_i;uint len;while(j!0){len;j /10;}bytes memory bstrnew bytes(len);uint klen;while(_i!0){kk-1;uint8 temp(48 uint8(_i - _i /10*10));bytes1 b1bytes1(temp);bstr[k]b1;_i /10;}returnstring(bstr);}}7. 抽象合约7.1 什么是抽象合约抽象合约Abstract Contract是包含至少一个未实现函数的合约。定义语法abstract contract 合约名{// 至少一个未实现的函数}基本示例abstract contract Animal{// 抽象函数只有声明没有实现functionmakeSound()public virtual returns(string memory);// 普通函数可以有实现functionsleep()public pure returns(string memory){returnZzz...;}// 可以有状态变量 uint256 public age;}// 实现抽象合约 contract Dog is Animal{// 必须实现makeSoundfunctionmakeSound()public pure override returns(string memory){returnWoof!;}}contract Cat is Animal{functionmakeSound()public pure override returns(string memory){returnMeow!;}}抽象合约的特点不能直接部署必须被继承可以有实现部分函数可以有实现可以有状态变量可以定义状态变量可以有构造函数可以有构造函数强制实现子合约必须实现所有抽象函数8. 接口8.1 什么是接口接口Interface是纯粹的接口定义只声明函数签名不包含任何实现。定义语法interface 接口名{// 只有函数声明}基本示例interface ICounter{// 所有函数必须是externalfunctiongetCount()external view returns(uint256);functionincrement()external;functiondecrement()external;// 可以定义事件 event CountChanged(uint256 newCount);}// 实现接口 contract Counter is ICounter{uint256 private count;event CountChanged(uint256 newCount);functiongetCount()external view override returns(uint256){returncount;}functionincrement()external override{count;emit CountChanged(count);}functiondecrement()external override{count--;emit CountChanged(count);}}8.2 接口的特点和限制接口的特点使用interface关键字不能有实现所有函数都是声明不能有状态变量不能定义storage变量不能有构造函数所有函数必须external可以继承其他接口可以定义事件接口vs合约对比8.3 ERC20接口标准ERC20是最经典的接口定义示例。interface IERC20{// 查询函数functiontotalSupply()external view returns(uint256);functionbalanceOf(address account)external view returns(uint256);functionallowance(address owner, address spender)external view returns(uint256);// 操作函数functiontransfer(address to, uint256 amount)external returns(bool);functionapprove(address spender, uint256 amount)external returns(bool);functiontransferFrom(address from, address to, uint256 amount)external returns(bool);// 事件 event Transfer(address indexed from, address indexed to, uint256 value);event Approval(address indexed owner, address indexed spender, uint256 value);}8.4 接口用于合约交互接口最重要的应用是合约间交互。场景合约A调用合约B// 定义接口 interface IToken{functiontransfer(address to, uint256 amount)external returns(bool);functionbalanceOf(address account)external view returns(uint256);}// 使用接口与其他合约交互 contract Exchanger{functionswapTokens(address tokenAddress, address recipient, uint256 amount)public{// 通过接口与代币合约交互 IToken tokenIToken(tokenAddress);// 检查余额 uint256 balancetoken.balanceOf(address(this));require(balanceamount,Insufficient balance);// 执行转账 bool successtoken.transfer(recipient, amount);require(success,Transfer failed);}}接口的优势解耦不需要知道合约的完整代码标准化统一的接口规范互操作性不同合约可以互相调用节省gas不需要导入完整合约代码