【从0学习Solidity】 40. ERC1155

news2024/11/25 6:56:53

【从0学习Solidity】40. ERC1155

在这里插入图片描述

  • 博主简介:不写代码没饭吃,一名全栈领域的创作者,专注于研究互联网产品的解决方案和技术。熟悉云原生、微服务架构,分享一些项目实战经验以及前沿技术的见解。
  • 关注我们的主页,探索全栈开发,期待与您一起在移动开发的世界中,不断进步和创造!
  • 本文收录于 不写代码没饭吃 的学习汇报系列,大家有兴趣的可以看一看。
  • 欢迎访问我们的微信公众号:不写代码没饭吃,获取更多精彩内容、实用技巧、行业资讯等。您关注的是我们前进的动力!

这一讲,我们将学习ERC1155标准,它支持一个合约包含多种代币。并且,我们会发行一个魔改的无聊猿 - BAYC1155:它包含10,000种代币,且元数据与BAYC一致。

EIP1155

不论是ERC20还是ERC721标准,每个合约都对应一个独立的代币。假设我们要在以太坊上打造一个类似《魔兽世界》的大型游戏,这需要我们对每个装备都部署一个合约。上千种装备就要部署和管理上千个合约,这非常麻烦。因此,以太坊EIP1155提出了一个多代币标准ERC1155,允许一个合约包含多个同质化和非同质化代币。ERC1155在GameFi应用最多,Decentraland、Sandbox等知名链游都使用它。

简单来说,ERC1155与之前介绍的非同质化代币标准ERC721类似:在ERC721中,每个代币都有一个tokenId作为唯一标识,每个tokenId只对应一个代币;而在ERC1155中,每一种代币都有一个id作为唯一标识,每个id对应一种代币。这样,代币种类就可以非同质的在同一个合约里管理了,并且每种代币都有一个网址uri来存储它的元数据,类似ERC721tokenURI。下面是ERC1155的元数据接口合约IERC1155MetadataURI

/**
 * @dev ERC1155的可选接口,加入了uri()函数查询元数据
 */
interface IERC1155MetadataURI is IERC1155 {
    /**
     * @dev 返回第`id`种类代币的URI
     */
    function uri(uint256 id) external view returns (string memory);

那么怎么区分ERC1155中的某类代币是同质化还是非同质化代币呢?其实很简单:如果某个id对应的代币总量为1,那么它就是非同质化代币,类似ERC721;如果某个id对应的代币总量大于1,那么他就是同质化代币,因为这些代币都分享同一个id,类似ERC20

IERC1155接口合约

IERC1155接口合约抽象了EIP1155需要实现的功能,其中包含4个事件和6个函数。与ERC721不同,因为ERC1155包含多类代币,它实现了批量转账和批量余额查询,一次操作多种代币。

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "https://github.com/AmazingAng/WTFSolidity/blob/main/34_ERC721/IERC165.sol";

/**
 * @dev ERC1155标准的接口合约,实现了EIP1155的功能
 * 详见:https://eips.ethereum.org/EIPS/eip-1155[EIP].
 */
interface IERC1155 is IERC165 {
    /**
     * @dev 单类代币转账事件
     * 当`value`个`id`种类的代币被`operator`从`from`转账到`to`时释放.
     */
    event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);

    /**
     * @dev 批量代币转账事件
     * ids和values为转账的代币种类和数量数组
     */
    event TransferBatch(
        address indexed operator,
        address indexed from,
        address indexed to,
        uint256[] ids,
        uint256[] values
    );

    /**
     * @dev 批量授权事件
     * 当`account`将所有代币授权给`operator`时释放
     */
    event ApprovalForAll(address indexed account, address indexed operator, bool approved);

    /**
     * @dev 当`id`种类的代币的URI发生变化时释放,`value`为新的URI
     */
    event URI(string value, uint256 indexed id);

    /**
     * @dev 持仓查询,返回`account`拥有的`id`种类的代币的持仓量
     */
    function balanceOf(address account, uint256 id) external view returns (uint256);

    /**
     * @dev 批量持仓查询,`accounts`和`ids`数组的长度要想等。
     */
    function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids)
        external
        view
        returns (uint256[] memory);

    /**
     * @dev 批量授权,将调用者的代币授权给`operator`地址。
     * 释放{ApprovalForAll}事件.
     */
    function setApprovalForAll(address operator, bool approved) external;

    /**
     * @dev 批量授权查询,如果授权地址`operator`被`account`授权,则返回`true`
     * 见 {setApprovalForAll}函数.
     */
    function isApprovedForAll(address account, address operator) external view returns (bool);

    /**
     * @dev 安全转账,将`amount`单位`id`种类的代币从`from`转账给`to`.
     * 释放{TransferSingle}事件.
     * 要求:
     * - 如果调用者不是`from`地址而是授权地址,则需要得到`from`的授权
     * - `from`地址必须有足够的持仓
     * - 如果接收方是合约,需要实现`IERC1155Receiver`的`onERC1155Received`方法,并返回相应的值
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes calldata data
    ) external;

    /**
     * @dev 批量安全转账
     * 释放{TransferBatch}事件
     * 要求:
     * - `ids`和`amounts`长度相等
     * - 如果接收方是合约,需要实现`IERC1155Receiver`的`onERC1155BatchReceived`方法,并返回相应的值
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] calldata ids,
        uint256[] calldata amounts,
        bytes calldata data
    ) external;
}

IERC1155事件

  • TransferSingle事件:单类代币转账事件,在单币种转账时释放。
  • TransferBatch事件:批量代币转账事件,在多币种转账时释放。
  • ApprovalForAll事件:批量授权事件,在批量授权时释放。
  • URI事件:元数据地址变更事件,在uri变化时释放。

IERC1155函数

  • balanceOf():单币种余额查询,返回account拥有的id种类的代币的持仓量。
  • balanceOfBatch():多币种余额查询,查询的地址accounts数组和代币种类ids数组的长度要相等。
  • setApprovalForAll():批量授权,将调用者的代币授权给operator地址。。
  • isApprovedForAll():查询批量授权信息,如果授权地址operatoraccount授权,则返回true
  • safeTransferFrom():安全单币转账,将amount单位id种类的代币从from地址转账给to地址。如果to地址是合约,则会验证是否实现了onERC1155Received()接收函数。
  • safeBatchTransferFrom():安全多币转账,与单币转账类似,只不过转账数量amounts和代币种类ids变为数组,且长度相等。如果to地址是合约,则会验证是否实现了onERC1155BatchReceived()接收函数。

ERC1155接收合约

ERC721标准类似,为了避免代币被转入黑洞合约,ERC1155要求代币接收合约继承IERC1155Receiver并实现两个接收函数:

  • onERC1155Received():单币转账接收函数,接受ERC1155安全转账safeTransferFrom 需要实现并返回自己的选择器0xf23a6e61

  • onERC1155BatchReceived():多币转账接收函数,接受ERC1155安全多币转账safeBatchTransferFrom 需要实现并返回自己的选择器0xbc197c81

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "https://github.com/AmazingAng/WTFSolidity/blob/main/34_ERC721/IERC165.sol";

/**
 * @dev ERC1155接收合约,要接受ERC1155的安全转账,需要实现这个合约
 */
interface IERC1155Receiver is IERC165 {
    /**
     * @dev 接受ERC1155安全转账`safeTransferFrom` 
     * 需要返回 0xf23a6e61 或 `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
     */
    function onERC1155Received(
        address operator,
        address from,
        uint256 id,
        uint256 value,
        bytes calldata data
    ) external returns (bytes4);

    /**
     * @dev 接受ERC1155批量安全转账`safeBatchTransferFrom` 
     * 需要返回 0xbc197c81 或 `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
     */
    function onERC1155BatchReceived(
        address operator,
        address from,
        uint256[] calldata ids,
        uint256[] calldata values,
        bytes calldata data
    ) external returns (bytes4);
}

ERC1155主合约

ERC1155主合约实现了IERC1155接口合约规定的函数,还有单币/多币的铸造和销毁函数。

ERC1155变量

ERC1155主合约包含4个状态变量:

  • name:代币名称
  • symbol:代币代号
  • _balances:代币持仓映射,记录代币种类id下某地址account的持仓量balances
  • _operatorApprovals:批量授权映射,记录持有地址给另一个地址的授权情况。

ERC1155函数

ERC1155主合约包含16个函数:

  • 构造函数:初始化状态变量namesymbol
  • supportsInterface():实现ERC165标准,声明它支持的接口,供其他合约检查。
  • balanceOf():实现IERC1155balanceOf(),查询持仓量。与ERC721标准不同,这里需要输入查询的持仓地址account以及币种id
  • balanceOfBatch():实现IERC1155balanceOfBatch(),批量查询持仓量。
  • setApprovalForAll():实现IERC1155setApprovalForAll(),批量授权,释放ApprovalForAll事件。
  • isApprovedForAll():实现IERC1155isApprovedForAll(),查询批量授权信息。
  • safeTransferFrom():实现IERC1155safeTransferFrom(),单币种安全转账,释放TransferSingle事件。与ERC721不同,这里不仅需要填发出方from,接收方to,代币种类id,还需要填转账数额amount
  • safeBatchTransferFrom():实现IERC1155safeBatchTransferFrom(),多币种安全转账,释放TransferBatch事件。
  • _mint():单币种铸造函数。
  • _mintBatch():多币种铸造函数。
  • _burn():单币种销毁函数。
  • _burnBatch():多币种销毁函数。
  • _doSafeTransferAcceptanceCheck:单币种转账的安全检查,被safeTransferFrom()调用,确保接收方为合约的情况下,实现了onERC1155Received()函数。
  • _doSafeBatchTransferAcceptanceCheck:多币种转账的安全检查,,被safeBatchTransferFrom调用,确保接收方为合约的情况下,实现了onERC1155BatchReceived()函数。
  • uri():返回ERC1155的第id种代币存储元数据的网址,类似ERC721tokenURI
  • baseURI():返回baseURIuri就是把baseURIid拼接在一起,需要开发重写。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "./IERC1155.sol";
import "./IERC1155Receiver.sol";
import "./IERC1155MetadataURI.sol";
import "https://github.com/AmazingAng/WTFSolidity/blob/main/34_ERC721/Address.sol";
import "https://github.com/AmazingAng/WTFSolidity/blob/main/34_ERC721/String.sol";
import "https://github.com/AmazingAng/WTFSolidity/blob/main/34_ERC721/IERC165.sol";

/**
 * @dev ERC1155多代币标准
 * 见 https://eips.ethereum.org/EIPS/eip-1155
 */
contract ERC1155 is IERC165, IERC1155, IERC1155MetadataURI {
    using Address for address; // 使用Address库,用isContract来判断地址是否为合约
    using Strings for uint256; // 使用String库
    // Token名称
    string public name;
    // Token代号
    string public symbol;
    // 代币种类id 到 账户account 到 余额balances 的映射
    mapping(uint256 => mapping(address => uint256)) private _balances;
    // address 到 授权地址 的批量授权映射
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    /**
     * 构造函数,初始化`name` 和`symbol`, uri_
     */
    constructor(string memory name_, string memory symbol_) {
        name = name_;
        symbol = symbol_;
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return
            interfaceId == type(IERC1155).interfaceId ||
            interfaceId == type(IERC1155MetadataURI).interfaceId ||
            interfaceId == type(IERC165).interfaceId;
    }

    /**
     * @dev 持仓查询 实现IERC1155的balanceOf,返回account地址的id种类代币持仓量。
     */
    function balanceOf(address account, uint256 id) public view virtual override returns (uint256) {
        require(account != address(0), "ERC1155: address zero is not a valid owner");
        return _balances[id][account];
    }

    /**
     * @dev 批量持仓查询
     * 要求:
     * - `accounts` 和 `ids` 数组长度相等.
     */
    function balanceOfBatch(address[] memory accounts, uint256[] memory ids)
        public view virtual override
        returns (uint256[] memory)
    {
        require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch");
        uint256[] memory batchBalances = new uint256[](accounts.length);
        for (uint256 i = 0; i < accounts.length; ++i) {
            batchBalances[i] = balanceOf(accounts[i], ids[i]);
        }
        return batchBalances;
    }

    /**
     * @dev 批量授权,调用者授权operator使用其所有代币
     * 释放{ApprovalForAll}事件
     * 条件:msg.sender != operator
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        require(msg.sender != operator, "ERC1155: setting approval status for self");
        _operatorApprovals[msg.sender][operator] = approved;
        emit ApprovalForAll(msg.sender, operator, approved);
    }

    /**
     * @dev 查询批量授权.
     */
    function isApprovedForAll(address account, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[account][operator];
    }

    /**
     * @dev 安全转账,将`amount`单位的`id`种类代币从`from`转账到`to`
     * 释放 {TransferSingle} 事件.
     * 要求:
     * - to 不能是0地址.
     * - from拥有足够的持仓量,且调用者拥有授权
     * - 如果 to 是智能合约, 他必须支持 IERC1155Receiver-onERC1155Received.
     */
    function safeTransferFrom(
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) public virtual override {
        address operator = msg.sender;
        // 调用者是持有者或是被授权
        require(
            from == operator || isApprovedForAll(from, operator),
            "ERC1155: caller is not token owner nor approved"
        );
        require(to != address(0), "ERC1155: transfer to the zero address");
        // from地址有足够持仓
        uint256 fromBalance = _balances[id][from];
        require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
        // 更新持仓量
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }
        _balances[id][to] += amount;
        // 释放事件
        emit TransferSingle(operator, from, to, id, amount);
        // 安全检查
        _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data);    
    }

    /**
     * @dev 批量安全转账,将`amounts`数组单位的`ids`数组种类代币从`from`转账到`to`
     * 释放 {TransferSingle} 事件.
     * 要求:
     * - to 不能是0地址.
     * - from拥有足够的持仓量,且调用者拥有授权
     * - 如果 to 是智能合约, 他必须支持 IERC1155Receiver-onERC1155BatchReceived.
     * - ids和amounts数组长度相等
     */
    function safeBatchTransferFrom(
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) public virtual override {
        address operator = msg.sender;
        // 调用者是持有者或是被授权
        require(
            from == operator || isApprovedForAll(from, operator),
            "ERC1155: caller is not token owner nor approved"
        );
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");
        require(to != address(0), "ERC1155: transfer to the zero address");

        // 通过for循环更新持仓  
        for (uint256 i = 0; i < ids.length; ++i) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: insufficient balance for transfer");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
            _balances[id][to] += amount;
        }

        emit TransferBatch(operator, from, to, ids, amounts);
        // 安全检查
        _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data);    
    }

    /**
     * @dev 铸造
     * 释放 {TransferSingle} 事件.
     */
    function _mint(
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) internal virtual {
        require(to != address(0), "ERC1155: mint to the zero address");

        address operator = msg.sender;

        _balances[id][to] += amount;
        emit TransferSingle(operator, address(0), to, id, amount);

        _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data);
    }

    /**
     * @dev 批量铸造
     * 释放 {TransferBatch} 事件.
     */
    function _mintBatch(
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) internal virtual {
        require(to != address(0), "ERC1155: mint to the zero address");
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        address operator = msg.sender;

        for (uint256 i = 0; i < ids.length; i++) {
            _balances[ids[i]][to] += amounts[i];
        }

        emit TransferBatch(operator, address(0), to, ids, amounts);

        _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data);
    }

    /**
     * @dev 销毁
     */
    function _burn(
        address from,
        uint256 id,
        uint256 amount
    ) internal virtual {
        require(from != address(0), "ERC1155: burn from the zero address");

        address operator = msg.sender;

        uint256 fromBalance = _balances[id][from];
        require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
        unchecked {
            _balances[id][from] = fromBalance - amount;
        }

        emit TransferSingle(operator, from, address(0), id, amount);
    }

    /**
     * @dev 批量销毁
     */
    function _burnBatch(
        address from,
        uint256[] memory ids,
        uint256[] memory amounts
    ) internal virtual {
        require(from != address(0), "ERC1155: burn from the zero address");
        require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch");

        address operator = msg.sender;

        for (uint256 i = 0; i < ids.length; i++) {
            uint256 id = ids[i];
            uint256 amount = amounts[i];

            uint256 fromBalance = _balances[id][from];
            require(fromBalance >= amount, "ERC1155: burn amount exceeds balance");
            unchecked {
                _balances[id][from] = fromBalance - amount;
            }
        }

        emit TransferBatch(operator, from, address(0), ids, amounts);
    }

    // @dev ERC1155的安全转账检查
    function _doSafeTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256 id,
        uint256 amount,
        bytes memory data
    ) private {
        if (to.isContract()) {
            try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) {
                if (response != IERC1155Receiver.onERC1155Received.selector) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non-ERC1155Receiver implementer");
            }
        }
    }

    // @dev ERC1155的批量安全转账检查
    function _doSafeBatchTransferAcceptanceCheck(
        address operator,
        address from,
        address to,
        uint256[] memory ids,
        uint256[] memory amounts,
        bytes memory data
    ) private {
        if (to.isContract()) {
            try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns (
                bytes4 response
            ) {
                if (response != IERC1155Receiver.onERC1155BatchReceived.selector) {
                    revert("ERC1155: ERC1155Receiver rejected tokens");
                }
            } catch Error(string memory reason) {
                revert(reason);
            } catch {
                revert("ERC1155: transfer to non-ERC1155Receiver implementer");
            }
        }
    }

    /**
     * @dev 返回ERC1155的id种类代币的uri,存储metadata,类似ERC721的tokenURI.
     */
    function uri(uint256 id) public view virtual override returns (string memory) {
        string memory baseURI = _baseURI();
        return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, id.toString())) : "";
    }

    /**
     * 计算{uri}的BaseURI,uri就是把baseURI和tokenId拼接在一起,需要开发重写.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return "";
    }
}

BAYC,但是ERC1155

我们魔改下ERC721标准的无聊猿BAYC,创建一个免费铸造的BAYC1155。我们修改_baseURI()函数,使得BAYC1155uriBAYCtokenURI一样。这样,BAYC1155元数据会与无聊猿的相同:

// SPDX-License-Identifier: MIT
// by 0xAA
pragma solidity ^0.8.4;

import "./ERC1155.sol";

contract BAYC1155 is ERC1155{
    uint256 constant MAX_ID = 10000; 
    // 构造函数
    constructor() ERC1155("BAYC1155", "BAYC1155"){
    }

    //BAYC的baseURI为ipfs://QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariGR3jxcaWtq/ 
    function _baseURI() internal pure override returns (string memory) {
        return "ipfs://QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariGR3jxcaWtq/";
    }
    
    // 铸造函数
    function mint(address to, uint256 id, uint256 amount) external {
        // id 不能超过10,000
        require(id < MAX_ID, "id overflow");
        _mint(to, id, amount, "");
    }

    // 批量铸造函数
    function mintBatch(address to, uint256[] memory ids, uint256[] memory amounts) external {
        // id 不能超过10,000
        for (uint256 i = 0; i < ids.length; i++) {
            require(ids[i] < MAX_ID, "id overflow");
        }
        _mintBatch(to, ids, amounts, "");
    }
}

Remix演示

1. 部署BAYC1155合约

40-1.jpg

2. 查看元数据uri

40-2.jpg

3. mint并查看持仓变化

mint一栏中输入账户地址、id和数量,点击mint按钮铸造。若数量为1,则为非同质化代币;若数量大于1,则为同质化代币。

40-3.jpg

blanceOf一栏中输入账户地址和id查看对应持仓

40-4.jpg

4. 批量mint并查看持仓变化

mintBatch一栏中输入要铸造的ids数组以及对应的数量,两者数组的长度必须相等

40-5.jpg

将刚刚铸造好的代币id数组输入即可查看

40-6.jpg

5. 批量转账并查看持仓变化

与铸造类似,不过这次要从拥有相应代币的地址转到一个新的地址,这个地址可以是普通地址也可以是合约地址,如果是合约地址会验证是否实现了onERC1155Received()接收函数。

这里我们转给一个普通地址,输入idsamounts数组。

40-7.jpg

对刚才转入的地址查看其持仓变化。

40-8.jpg

总结

这一讲我们学习了以太坊EIP1155提出的ERC1155多代币标准,它允许一个合约中包含多个同质化或非同质化代币。并且,我们创建了魔改版无聊猿 - BAYC1155:一个包含10,000种代币且元数据与BAYC相同的ERC1155代币。目前,ERC1155主要应用于GameFi中。但我相信随着元宇宙技术不断发展,这个标准会越来越流行。

在这里插入图片描述

如果这份博客对大家有帮助,希望各位给作者一个免费的点赞👍作为鼓励,并评论收藏一下⭐,谢谢大家!!!
制作不易,如果大家有什么疑问或给作者的意见,欢迎评论区留言。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1036716.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《动手学深度学习 Pytorch版》 7.1 深度卷积神经网络(AlexNet)

7.1.1 学习表征 深度卷积神经网络的突破出现在2012年。突破可归因于以下两个关键因素&#xff1a; 缺少的成分&#xff1a;数据 数据集紧缺的情况在 2010 年前后兴起的大数据浪潮中得到改善。ImageNet 挑战赛中&#xff0c;ImageNet数据集由斯坦福大学教授李飞飞小组的研究人…

Spring后处理器-BeanPostProcessor

Spring后处理器-BeanPostProcessor Bean被实例化后&#xff0c;到最终缓存到名为singletonObjects单例池之前&#xff0c;中间会经过bean的初始化过程&#xff08;&#xff08;该后处理器的执行时机&#xff09;&#xff09;&#xff0c;例如&#xff1a;属性的填充、初始化方…

第 364 场 LeetCode 周赛题解

A 最大二进制奇数 降序排序字符串&#xff0c;然后将最后一个 1 与最后一位交换 class Solution { public:string maximumOddBinaryNumber(string s) {sort(s.begin(), s.end(), greater<>());for (int i s.size() - 1;; i--)if (s[i] 1) {swap(s[i], s.back());break;…

【Oracle】Oracle系列之八--SQL查询

文章目录 往期回顾前言1. 基本查询&#xff08;1&#xff09;All&#xff08;2&#xff09;in/exists 子查询&#xff08;3&#xff09;union/except/intersect&#xff08;4&#xff09;group by&#xff08;5&#xff09;having&#xff08;6&#xff09;聚集函数&#xff1a…

SLAM从入门到精通(用c++实现机器人运动控制)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 之前的一篇文章&#xff0c;我们知道了可以通过wpr_simulation包仿真出机器人和现场环境。如果需要控制机器人&#xff0c;这个时候就需要rqt_robo…

AcWing 5153. 删除(AcWing杯 - 周赛)(结论+枚举)

思路&#xff1a; ACcode: #include<bits/stdc.h> using namespace std; #define int long long string s; void solve() {cin>>s;s"00"s;int lens.size();for(int i0; i<len; i) {for(int ji1; j<len; j) {for(int kj1; k<len; k) {int xs[i]*…

leetcode:2446. 判断两个事件是否存在冲突(python3解法)

难度&#xff1a;简单 给你两个字符串数组 event1 和 event2 &#xff0c;表示发生在同一天的两个闭区间时间段事件&#xff0c;其中&#xff1a; event1 [startTime1, endTime1] 且event2 [startTime2, endTime2] 事件的时间为有效的 24 小时制且按 HH:MM 格式给出。 当两个…

Windows 基于Visual Studio 开发Qt 6 连接MySQL 8

前提条件&#xff1a; 1、Visual Studio 2022 社区版(免费版) 2、Qt-6.5.1版本 3、MySQL 8 Qt 6 配置MySQL 8 动态/静态连接库和MySQL 8 驱动。 libmysql.dll 和libmysql.lib是QT所需的动态和静态链接库&#xff1b;qsqlmysql.dll 和qsqlmysql.dll.debug是Qt所需的mysql驱…

机器人过程自动化(RPA)入门 1. 什么是机器人过程自动化?

如今&#xff0c;我们生活中几乎没有任何方面不受自动化的影响。一些例子包括洗衣机、微波炉、汽车和飞机的自动驾驶模式&#xff0c;雀巢在日本的商店里使用机器人销售咖啡豆&#xff0c;沃尔玛在美国测试无人机送货&#xff0c;我们的银行支票使用光学字符识别&#xff08;OC…

【Linux】调试代码的工具 - gdb

1、安装gdb sudo yum -y install gdb【安装gdb】 2、gdb的使用 在 Linux 下&#xff0c;我们编写代码默认以 release 方式发布如果想让代码以 debug 方式发布&#xff0c;必须给 gcc 添加 -g 选项 (gdb) q / quit【退出gdb】(gdb) l / list&#xff08;list可缩写为 l&#xf…

C++的文件操作

文件操作 程序运行时产生的数据都属于临时数据&#xff0c;通过文件可将数据持久化 C中对文件操作需要包含头文件<fstream> 文件类型分为两种&#xff1a; 文本文件 - 文件以文本的ASCII码形式存储在计算机中二进制文件 - 文件以文本的二进制形式存储在计算机中&…

如何取消自动播放音乐:取消手机汽车连上后汽车自动播放音乐?

背景 手机和汽车通过蓝牙连接上之后&#xff0c;汽车音响会自动播放手机上的音乐&#xff0c;似乎是自动唤醒APP的&#xff0c;因为这些音乐APP在手机上是已经被杀了后台的了。 而且汽车的屏幕的播放列表里头会显示播放的音乐的名称&#xff0c;也有可能是视频的名称&#xf…

安卓备份分区----手动查询安卓系统分区信息 导出系统分区的一些基本操作

在玩机搞机过程中。有时候需要手动查看有些分区信息&#xff0c;或者备份分区的操作。那么今天以小米8为例解析下其中的操作步骤 机型&#xff1a;小米8 adb版本&#xff1a;https://developer.android.com/studio/releases/platform-tools 机型芯片&#xff1a;高通骁龙845…

基于微信小程序的校园商铺系统,附源码、数据库

文章目录 第一章 简介第二章 技术栈第三章&#xff1a;总体设计第四章系统详细设计4.1 前台功能模块4.2后台功能模块4.2.1管理员功能模块 五 源码咨询 第一章 简介 今天&#xff0c;为大家带来的事基于微信小程序的校园商铺系统。本系统的主要意义在于&#xff0c;全力以赴为用…

Redis双写一致性、持久化机制、分布式锁

一.双写一致性: 含义:当数据库中的数据被修改了以后&#xff0c;我们也需要同时修改缓存&#xff0c;使缓存和数据库的数据保持一致 &#xff08;1&#xff09;读操作:当请求发来的时候&#xff0c;先去看redis里面是否有对应的数据&#xff0c;如果有直接返回&#xff0c;如果…

轻量级的日志采集组件 Filebeat 讲解与实战操作

文章目录 一、概述二、Kafka 安装三、Filebeat 安装1&#xff09;下载 Filebeat2&#xff09;Filebeat 配置参数讲解3&#xff09;filebeat.prospectors 推送kafka完整配置1、filebeat.prospectors2、processors3、output.kafka 4&#xff09;filebeat.inputs 与 filebeat.pros…

【STL】vector常见用法及模拟实现(附源码)

目录 前言1. vector介绍及使用1.1vector的介绍1.2 vector的使用1.2.1 构造函数 1.2.2 vector对象遍历1.2.3 reserve和resize1.2.4 insert和erase 2. vector模拟实现2.1 vector迭代器失效问题2.2 模拟实现reserve函数浅拷贝问题2.3模拟实现源码2.3.1 vector.h2.3.2 test.cpp 前言…

org.postgresql.util.PSQLException: Bad value for type long

项目用 springbootmybatis mybatisplus&#xff0c; 数据库是&#xff1a;postgresql 。 执行查询时候返回错误。 org.springframework.dao.DataIntegrityViolationException: Error attempting to get column city_id from result set. Cause: org.postgresql.util.PSQLExce…

如何让ChatGPT为留学生所用?

“我们这一届学Data Analyics和Data Science的没一个找到工作的。”朋友饭桌上的闲话让研究生才算踏入DA圈子的我瑟瑟发抖。 还没开始正式求职的我&#xff0c;似乎已经被宣告失业了。而这一切都要“归功”于以ChatGPT为代表的大语言模型&#xff08;LLMs&#xff09;。 问世不…

接口测试练习步骤

在接触接口测试过程中补了很多课&#xff0c; 终于有点领悟接口测试的根本&#xff1b; 偶是个实用派&#xff5e;&#xff0c;那么现实中没有用的东西&#xff0c;基本上我都不会有很大的概念&#xff1b; 下面给的是接口测试的统一大步骤&#xff0c;其实就是让我们对接口…