在写了一个业务合约时,发现被继承合约含有构造函数时报错,去掉被继承合约中构造函数正常,有些奇怪,于是写一个简单例子进行复现,合约代码如下:
- // SPDX-License-Identifier: MIT
- pragma solidity ^0.8.7;
-
- //基合约接囗
- interface IcontractA {
- function getMsg() external view returns(string memory);
- }
-
- //基合约实现
- contract contractA is IcontractA {
- string message;
-
- //构造函数
- constructor(string memory _message) {
- message = _message;
- }
-
- function _getMsg() internal view returns (string memory) {
- return message;
- }
-
- function getMsg() external override view returns(string memory) {
- return _getMsg();
- }
- }
-
- //子合约 - 继承基合约
- contract contractB is contractA {
- address admin;
-
- //构造函数
- constructor(string memory _message) {
- admin = msg.sender;
- }
-
- //调用基合约函数
- function callA() external view returns(string memory) {
- return _getMsg();
- }
- }
错误信息
TypeError: Contract "contractB" should be marked as abstract. --> Test/CallContract.sol:28:1: | 28 | contract contractB is contractA { | ^ (Relevant source part starts here and spans across multiple lines). Note: Missing implementation: --> Test/CallContract.sol:14:5: | 14 | constructor(string memory _message) { | ^ (Relevant source part starts here and spans across multiple lines).
Remix浏览器执行结果:

当继承合约时,派生合约(子合约)需要提供基类构造函数需要的所有参数,参见 合约 — Solidity develop 文档
在派生合约构造函数中添加基合约所需要的参数
32行调整如下:
constructor(string memory _message) contractA(_message) {
修正后合约代码如下:
- // SPDX-License-Identifier: MIT
- pragma solidity ^0.8.7;
-
- //基合约接囗
- interface IcontractA {
- function getMsg() external view returns(string memory);
- }
-
- //基合约实现
- contract contractA is IcontractA {
- string message;
-
- //构造函数
- constructor(string memory _message) {
- message = _message;
- }
-
- function _getMsg() internal view returns (string memory) {
- return message;
- }
-
- function getMsg() external override view returns(string memory) {
- return _getMsg();
- }
- }
-
- //子合约 - 继承基合约
- contract contractB is contractA {
- address admin;
-
- //构造函数
- constructor(string memory _message) contractA(_message) {
- admin = msg.sender;
- }
-
- //调用基合约函数
- function callA() external view returns(string memory) {
- return _getMsg();
- }
- }
如果基合约构造函数无参数,在派生合约中也需要提供基合约构造函数,只是无参而已,微调上面代码,基合约构造函数设置无参
32行调整如下:
constructor(string memory _message) contractA() {
- // SPDX-License-Identifier: MIT
- pragma solidity ^0.8.7;
-
- //基合约接囗
- interface IcontractA {
- function getMsg() external view returns(string memory);
- }
-
- //基合约实现
- contract contractA is IcontractA {
- string message;
-
- //构造函数
- constructor() {
- message = "_message";
- }
-
- function _getMsg() internal view returns (string memory) {
- return message;
- }
-
- function getMsg() external override view returns(string memory) {
- return _getMsg();
- }
- }
-
- //子合约 - 继承基合约
- contract contractB is contractA {
- address admin;
-
- //构造函数
- constructor() contractA() {
- admin = msg.sender;
- }
-
- //调用基合约函数
- function callA() external view returns(string memory) {
- return _getMsg();
- }
- }
当子合约中创建一个基合约,通过新创建的基合约调用自己的查询函数(使用view修饰符)时,子合约中的函数不能使用view修饰符,合约代码如下:
- // SPDX-License-Identifier: MIT
- pragma solidity ^0.8.7;
-
- //基合约接囗
- interface IcontractA {
- function getMsg() external view returns(string memory);
- }
-
- //基合约实现
- contract contractA is IcontractA {
- string message;
-
- //构造函数
- constructor(string memory _message) {
- message = _message;
- }
-
- function _getMsg() internal view returns (string memory) {
- return message;
- }
-
- function getMsg() external override view returns (string memory) {
- return _getMsg();
- }
- }
-
- //子合约 - 继承基合约
- contract contractB is contractA {
- address admin;
-
- //构造函数
- constructor(string memory _message) contractA(_message) {
- admin = msg.sender;
- }
-
- //调用基合约函数
- function callA() external view returns (string memory) {
- return _getMsg();
- }
-
- //创建基合约,调用函数
- function callANewContract() external view returns (string memory) {
- contractA contractAddr = new contractA("Hello, I am tracy");
- return contractA(contractAddr).getMsg();
- }
- }
错误信息如下:
TypeError: Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable. --> Test/CallContract.sol:43:34: | 43 | contractA contractAddr = new contractA("Hello, I am tracy"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

去除42行中的view修饰符,部署合约B,发现调用函数callANewContract()居然是收取gas费的函数(黄色背景标识为收取gas费函数),如下图所示:
