【区块链安全 | 第三十篇】合约(四)

在这里插入图片描述

合约

继承

Solidity 支持多重继承,包括多态性。

多态性意味着函数调用(无论是内部的还是外部的)总是会执行继承体系中最底层合约中具有相同名称(和参数类型)的函数。这必须在继承体系中的每一个函数上显式启用,使用 virtualoverride 关键字。

可以通过显式指定合约名调用继承体系中更高层的函数,例如:ContractName.functionName(),或者使用 super.functionName() 调用继承结构中上一级的函数(参考下文中“扁平化继承结构”)。

当一个合约继承其他合约时,只会在区块链上创建一个合约实例,所有父合约的代码会被编译进这个创建的合约中。这意味着对父合约函数的所有内部调用只是普通的内部函数调用(例如 super.f(...) 会使用 JUMP 而不是消息调用)。

状态变量遮蔽(shadowing)会被视为错误:如果任何一个父合约中已经声明了变量 x,子合约中就不能再声明同名变量。

Solidity 的继承系统整体上类似 Python 的继承,尤其是对多重继承的处理,但也存在一些差异。

具体示例如下:

// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;

contract Owned {
    address payable owner;
    constructor() { owner = payable(msg.sender); }
}

// 使用 `is` 关键字派生自另一个合约。
// 派生合约可以访问所有非 private 的成员,包括 internal 函数和状态变量。
// 这些成员无法通过 `this` 关键字从外部访问。
contract Emittable is Owned {
    event Emitted();

    // 使用 `virtual` 关键字表示该函数可以被子类重写。
    function emitEvent() virtual public {
        if (msg.sender == owner)
            emit Emitted();
    }
}

// 这些抽象合约只是为了让编译器知道接口。注意没有函数体的声明。
// 如果一个合约未实现所有函数,就只能作为接口使用。
abstract contract Config {
    function lookup(uint id) public virtual returns (address adr);
}

abstract contract NameReg {
    function register(bytes32 name) public virtual;
    function unregister() public virtual;
}

// 支持多重继承。注意:`Owned` 同时是 `Emittable` 的父类,
// 但在最终合约中只会有一个 `Owned` 实例(类似 C++ 的虚继承)。
contract Named is Owned, Emittable {
    constructor(bytes32 name) {
        Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
        NameReg(config.lookup(1)).register(name);
    }

    // 函数可以通过相同的名称和参数类型进行重写。
    // 如果返回值类型不一样,将导致编译错误。
    // 无论是内部调用还是消息调用,都会考虑重写关系。
    // 如果你要重写函数,必须使用 `override` 关键字。
    // 如果这个函数还需要被其他合约重写,则还需要再次声明 `virtual`。
    function emitEvent() public virtual override {
        if (msg.sender == owner) {
            Config config = Config(0xD5f9D8D94886E70b06E474c3fB14Fd43E2f23970);
            NameReg(config.lookup(1)).unregister();
            // 仍然可以调用被重写的父类函数。
            Emittable.emitEvent();
        }
    }
}

// 如果构造函数需要参数,则必须在派生合约的构造器中提供,
// 可以在构造函数声明中传参,也可以通过修饰器风格传参。
contract PriceFeed is Owned, Emittable, Named("GoldFeed") {
    uint info;

    function updateInfo(uint newInfo) public {
        if (msg.sender == owner) info = newInfo;
    }

    // 这里只声明 `override` 而不是 `virtual`,
    // 意味着继承自 PriceFeed 的

猜你喜欢

转载自blog.csdn.net/2301_77485708/article/details/147039870
今日推荐