Solidity programming essentials-编写智能合约
本章内容
在本章中,您将开始编写真正的智能合约。它将讨论智能合约的设计方面,包括定义和实现合约,以及使用不同的机制(例如使用 new关键字和使用已知地址)部署和创建合约。
Solidity提供了丰富的面向对象特性,本章将深入探讨面向对象的概念和实现,例如继承、多重继承、声明抽象类和接口,以及为抽象函数和接口提供方法实现。
Solidity用于编写智能合约。本章专门介绍智能合约。您将从这里开始编写智能合约。本章将讨论智能合约的设计方面,包括合约的定义和实现,以及使用不同的机制(例如使用新的关键字和已知地址)来部署和创建合约。Solidity 提供了丰富的
面向对象特性,本章将深入探讨面向对象的概念和实现,例如继承、多重继承、声明抽象类和接口,以及为抽象函数和接口提供方法实现。
本章涵盖以下主题:
Creating contracts
Creating contracts via new
Inheritance
Abstract contracts
Interfaces
智能合约
什么是智能合约?每个人都试图理解合约的含义以及“智能”一词在合约语境中的意义,但往往一头雾水。智能合约本质上是部署在EVM(执行虚拟机)中的代码段或程序。合约一词通常用于法律领域,在编程领域意义不大。用Solidity编写智能合约并不意味着编写一份具有法律效力的合同。
此外,合约与其他任何程序代码一样,包含Solidity代码,并在被调用时执行。它本身并不智能。智能合约是区块链术语,指的是在EVM中执行的编程逻辑和代码。
智能合约与 C++、Java 或 C# 类非常相似。正如类由状态(变量)和行为(方法)组成一样,合约包含状态变量和函数。状态变量的作用是维护合约的当前状态,而函数负责执行逻辑并对当前状态进行更新和读取操作。
我们在上一章已经看到了一些智能合约的示例;
现在是时候深入探讨这个主题了。
编写一个简单的合约
合约使用 contract 关键字和一个标识符来声明,如下面的代码片段所示:
contract SampleContract { }
在括号内是状态变量和函数定义的声明。合约的完整定义已在第 3 章“Solidity 简介”中讨论过,我再次提供该定义,以便快速参考。
此合约包含状态变量、结构体、定义、枚举声明、函数定义、修饰符和事件。
状态变量、结构体和枚举已在第 4 章“全局变量和函数”中详细讨论过。
函数、修饰符和事件将在接下来的两章中详细讨论。
创建合约
在 Solidity 中,创建和使用合约有两种方法:
使用 new 关键字
使用已部署合约的地址
使用 new 关键字
Solidity 中的 new 关键字用于部署并创建一个新的合约实例。它通过部署合约、初始化状态变量、运行构造函数、将 nonce 值设置为 1 来初始化合约实例,最终将实例地址返回给调用者。部署合约包括检查请求者是否提供了足够的 gas 来完成部署,使用请求者的地址和 nonce 值生成一个新的账户/地址用于合约部署,并将随附的任何以太币传递给该账户/地址。
在下一张截图中,定义了两个合约:HelloWorld 和 client。
在这种情况下,一个合约(client)部署并创建了另一个合约(HelloWorld)的一个新实例。
它使用 new 关键字来实现这一点,如下面的代码片段所示:
HelloWorld myObj = new HelloWorld();
使用已部署合约的地址
当合约已部署并实例化时,可以使用此方法创建合约实例。此方法使用已部署合约的地址。不会创建新的实例;而是重用现有实例。
通过地址创建对现有合约的引用。
在下一个代码示例中,定义了两个合约:HelloWorld 和 client。
在这种情况下,一个合约(client)使用另一个合约(HelloWorld)的已知地址来创建对其的引用。
它使用地址数据类型并将实际地址强制转换为 HelloWorld 合约类型。
myObj 对象包含现有合约的地址,
如下面的代码片段所示:
构造函数(constructor)
Solidity 支持在合约中声明构造函数。
构造函数在 Solidity 中是可选的。如果开发者没有显式定义构造函数,编译器会自动生成一个默认构造函数。
构造函数在部署合约时执行一次。这与其他编程语言截然不同。
在其他语言中,每当创建一个对象实例时,构造函数都会被执行一次。
然而在 Solidity 中,构造函数仅在合约被部署到 EVM 的那一刻执行。构造函数通常用于初始化状态变量,并且一般应避免在其中书写过多或过于复杂的 Solidity 代码。
构造函数代码是一个合约生命周期中最先被执行的代码。
一个合约最多只能有一个构造函数,这与某些支持多个构造器重载的编程语言不同。
构造函数可以接受参数,这些参数应在部署合约时提供。
构造函数的名称应当与合约名称相同,两者必须一致。(注:这是 Solidity 0.4.x 的旧语法。新版已改为 constructor()。)
从可见性角度说,构造函数可以是 public 或 internal,但不能是 external 或 private。
(现代 Solidity 的构造函数不写可见性修饰符,默认是 public。)
构造函数不能显式返回任何数据。
在下面的示例中,定义了一个与 HelloWorld 合约同名的构造函数。它将存储变量 value 设置为 5。
当然可以 😊
下面是一个完整、适合在 Remix IDE 中直接运行的教学合约,
用于演示构造函数(constructor)的作用、参数传入、以及初始化状态变量的过程。
🧩 文件名:HelloWorld.sol
1 | |

🧠 学习要点解析
| 概念 | 说明 |
|---|---|
constructor |
构造函数,在合约部署时自动执行,仅执行一次。 |
_name, _value |
部署时传入的参数,用来初始化状态变量。 |
msg.sender |
部署合约的账户地址,会被存入 owner。 |
require |
确保只有合约部署者可以更新 value。 |
view |
表示只读函数,不会修改链上数据。 |
🧪 Remix 测试步骤
打开 Remix IDE
新建文件
HelloWorld.sol,粘贴上方代码选择编译器版本
0.8.24或更高点击 Deploy & Run Transactions
- 在部署栏输入:
name = "Solidity Demo"value = 5
- 在部署栏输入:
点击 Deploy
部署成功后,你可以在 Deployed Contracts 看到:
1 | |
🧭 可选扩展:多个构造函数的历史写法(仅了解)
旧版本 Solidity(0.4.x 以前)用“同名函数”来定义构造函数:
1 | |
在现代版本(≥0.5.0)中,这种写法已被弃用,
统一使用 constructor(...) {} 语法。
合约组合(Contract composition)
Solidity 支持合约组合。所谓“组合”,是指将多个合约或数据类型组合在一起,以创建更复杂的数据结构和合约。
我们之前已经看到过许多合约组合的例子。例如,在本章前面展示的、使用 new 关键字创建合约的代码片段中。
在 Solidity 中,将复杂问题拆解为多个合约,再通过合约组合将它们组织起来,是一种良好的实践。
当然可以 👍 我们可以把“父子关系”改成“继承关系”或“上层与下层合约关系”,这样就更中性、专业,也没有性别色彩。下面是改写后的版本:
继承(Inheritance)
继承是面向对象编程的核心概念之一,Solidity 同样支持智能合约之间的继承。
所谓继承,是指通过继承关系定义多个相互关联的合约。
被继承的合约称为基合约(base contract),
而继承它的合约称为派生合约(derived contract)。
继承的主要目的在于代码复用(code reusability)。
基合约与派生合约之间存在一种 “is-a”(是一个)关系,
并且所有 public 和 internal 作用域的函数与状态变量都可以被派生合约访问。
实际上,Solidity 编译器会将基合约的字节码复制到派生合约的字节码中。
在派生合约中使用关键字 is 来继承基合约。
继承是每一位 Solidity 开发者必须掌握的重要概念,
因为它直接影响合约的版本管理与部署方式。
Solidity 支持多种形式的继承,包括多重继承(multiple inheritance)。
编译时,Solidity 会将所有基合约的内容复制进派生合约,
最终生成一个完整的合约,并由系统分配一个唯一地址,
供所有具备继承关系的合约共享使用。
单一继承(Single inheritance)
单一继承是指一个派生合约(derived contract)只继承一个基合约(base contract)。
通过单一继承,派生合约可以继承基合约中的变量、函数、修饰器(modifier)以及事件(event),
从而实现代码复用与逻辑扩展。
可以想象成:“下层合约在上层合约的基础上继续发展功能”。
Solidity 示例:单一继承(Single Inheritance)
下面这个例子展示了一个简单的单一继承结构:
1 | |
🧩 说明
HelloWorld是 基合约,定义了一个状态变量message和一个函数sayHello()。HelloClient是 派生合约,通过is HelloWorld继承了HelloWorld的所有public与internal成员。- 在
HelloClient的构造函数中,通过HelloWorld(_msg)显式调用了基合约的构造函数。 HelloClient还定义了自己的新函数greet(),展示了在继承基础上扩展功能的用法。
🧠 总结:
单一继承让一个合约可以完整继承另一个合约的逻辑与数据,
既避免重复编写代码,又方便在此基础上扩展新特性。
Multi-level多层继承
多层继承与单层继承非常相似;不同的是,它不是只有一层“基合约与派生合约”的关系,而是存在多个层级的继承结构。
换句话说,一个合约可以继承自另一个,而那个合约又继承自更上层的合约,形成继承链。
1 | |
🧩 Solidity 示例:多层继承(Multi-level Inheritance)
1 | |
🧠 运行说明(在 Remix 中)

🧭 结构图
1 | |
层级继承(Hierarchical inheritance)与简单继承类似;
不过,在这种继承中,一个合约同时作为多个其他合约的基础合约。
如下图所示:
合约 A 被同时继承在合约 B 和合约 C 中。
1 | |
多重继承
Solidity 支持多重继承。可以存在多级单继承(即一个合约继承自另一个合约,然后再被另一个合约继承)。然而,也可以有多个合约从同一个基合约派生出来。这些派生合约可以在进一步的子合约中作为基合约一起使用。当合约同时继承自这些子合约时,就形成了多重继承,如下图所示:

✅ 小结
| 类型 | 示例结构 | 含义 |
|---|---|---|
| 单层继承 | Base → Derived | 一层扩展关系 |
| 多层继承 | Base → Intermediate → Final | 多个层级连续扩展 |
| 多重继承 | A, B → C | 从多个基础合约继承 |
| 菱形继承 | A→B, A→C, B+C→D | 需要线性化解决重复继承问题 |
封装
封装是面向对象编程(OOP)最重要的支柱之一。
封装指的是隐藏状态变量或控制对其的直接访问,以改变其状态的过程。它指的是一种模式,即声明变量,使客户端无法直接访问这些变量,只能通过函数进行修改。这有助于对变量访问进行约束,同时又允许类提供足够的访问接口以便执行操作。Solidity 提供了多种可见性修饰符(visibility modifiers),如 external、public、internal 和 private,它们会影响状态变量在合约内部、继承的子合约或外部合约中的可见性。