remix编译运行solidity5

Solidity 继承与 override 教学示例,涉及 virtual、override 和多继承的菱形问题。

TokenV1.sol

1
2
3
4
5
6
7
8
9
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract TokenV1 {
// 允许子合约重写
function version() public pure virtual returns (string memory) {
return "v1";
}
}

virtual:表示这个函数可以被, 子合约 重写(override)。

pure:不访问链上状态。

TokenV2.sol

1
2
3
4
5
6
7
8
9
10
11
12
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "./TokenV1.sol";

contract TokenV2 is TokenV1 {
// 重写父类函数
function version() public pure override returns (string memory) {
return "v2";
}
}

解释:

override:表示这个函数是 重写父类的 virtual 函数。

现在调用 TokenV2.version() 返回 “v2”,而 TokenV1.version() 仍返回 “v1”。

多继承的菱形问题是什么?

非常好的问题 👏
这是 Solidity 初学者在理解“继承”时最重要的难点之一

我们一步步拆开讲清楚——
什么是「多继承的菱形问题(Diamond Inheritance Problem)」、
为什么会出错、
Solidity 是如何解决的。


🧩 一、菱形继承是什么

假设我们有四个合约:

1
2
3
4
5
  A
/ \
B C
\ /
D

这就是所谓的「菱形继承结构」。

  • 合约 A 是最顶层父类
  • 合约 BC 都继承了 A
  • 合约 D 同时继承 BC

于是问题来了 ——
D 到底该继承 A 的版本 还是 B、C 的版本


💣 二、冲突示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

contract A {
function say() public pure virtual returns (string memory) {
return "A";
}
}

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

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

// ⚠️ 菱形继承
contract D is B, C {
// 必须明确指出要 override 哪些父类
function say() public pure override(B, C) returns (string memory) {
return "D";
}
}

⚙️ 三、问题原因

Solidity 不知道 D.say() 应该继承自哪个父类:

  • B 继承得到的 say() 返回 "B"
  • C 继承得到的 say() 返回 "C"

它们都源自 A,形成一条菱形路径:

1
2
3
4
5
6
7
8
9
A

├── B
│ \
│ \
│ D
│ /
│ /
└── C

如果没有显式指定,编译器会报错:

1
Derived contract must override function "say". Two or more base classes define function "say".

🧠 四、Solidity 如何解决

通过两个规则:

  1. 强制显式 override
    你必须写 override(B, C) 明确告诉编译器你要覆盖这两个父类。

  2. 线性化继承(C3 Linearization)
    Solidity 会确定唯一的父类调用顺序,
    D → C → B → A(从右到左的深度优先)。

👉 因此,如果你在 D 中写:

1
2
3
function say() public pure override(B, C) returns (string memory) {
return super.say();
}

那么执行顺序是:

1
D → C → BA

即最后会返回 "C"(因为从右往左继承,C 在右边优先)。


🧭 五、总结表

名称 含义
菱形继承 一个基类被两个路径间接继承
主要问题 函数来源不唯一(A 被重复继承)
解决方案 使用 override(A, B) + 线性化继承顺序
调用顺序 从右到左深度优先(super 调用链遵循 C3 线性化)

如果你想更直观看这个继承路径(比如在 Remix 或图中看到执行顺序),
我可以帮你画一张调用链示意图(箭头+函数流向),让你看清 super.say() 实际走哪条路径。