CTP 学习笔记

2023-11-19

CTP 学习笔记

前言

综合交易平台(Comprehensive Transaction Platform,CTP)是专门为期货公司开发的一套期货经纪业务管理系统,由交易、风险控制和结算三大系统组成。

前后研究了两个多星期 CTP,各种查资料,感觉总算是基本搞清楚了 CTP 是个什么东西(鬼知道我为什么要搞 CTP),说多了都是泪。本文主要通过对 CTP 简单案例的实现,对 CTP 进行简单的讲解,以及本人学习过程中遇到的一些坑。

下载安装

你可以在 上期技术官网 里面下载最新的 API 接口以及说明文档,或者也可以在 simnow 官网 下载,都是一样的,只不过上期技术官网会时不时抽风。

下载完成之后,我们得到了下面这些文件,以 win64 为例:

// 结构类型定义
ThostFtdcUserApiStruct.h        // 定义了客户端接口使用的业务数据结构
ThostFtdcUserApiDataType.h      // 定义了客户端接口使用的业务数据类型

// 行情部分
ThostFtdcMdApi.h                // 定义了客户端接口
thostmduserapi_se.dll           // 行情官方动态库
thostmduserapi_se.lib           // 行情官方静态库

// 交易部分
ThostFtdcTraderApi.h            // 定义了客户端接口
thosttraderapi_se.dll           // 交易官方动态库
thosttraderapi_se.lib           // 交易官方静态库

// 其他
error.dtd                       // 错误信息
error.xml                       // 错误信息

结构分析

总的来说,我们可以将 CTP 分为「行情部分」和「交易部分」

  • 行情部分:在行情部分中,我们可以实现「订阅行情」,从而获取「深度行情」,也就是最新价、成交量、持仓量等合约的信息,这也被称为 tick 数据。
  • 交易部分:在交易部分中,我们可以实现查询合约,查询仓位,下单,退单等交易的功能。

对于 API 的更多信息,你可以在官网上下载的 API 接口说明 中找到所有 API 和 SPI 的函数原型信息。(也可以点击这里下载

{% note info %}

CTP 的所有接口都分为 Spi 和 Api 两种,这里对其简单说明

API:Api 类提供了交易/行情的各种功能,但这些需要我们主动对服务器发出的请求

SPI:Spi 类提供了交易/行情相关的回调接口,我们需要继承该类并重载这些接口,以获取响应数据。

一般来说,API 和 SPI 都是配对出现的。

举个例子,我们为了实现登录,需要我们主动调用 API 函数 ReqUserLogin,我们暂且忽略参数,就像下面这个样子:

m_mdApi->ReqUserLogin(&t, 1)

在执行上面这条命令之后,我们向服务器发送 登录请求,服务器收到我们的请求之后,向我们发送 登录成功消息

之后对应的 SPI 回调函数 OnRspUserLogin 就会被调用。

void MdSpi::OnRspUserLogin(...)
{
  cout << "账户登录成功" << endl;
}

{% endnote %}

「行情部分」实现

在这里,我们以「行情部分」为例进行实现,「交易部分」的逻辑也是类似的。

继承 CThostFtdcMdSpi 类

首先,我们需要自己创建一个类,并继承 CThostFtdcMdSpi 类,同时需要我们自己重载 CThostFtdcMdSpi 中的回调函数,以便实现自己需要的功能。

我们新建一个名为 CTPDemo 的项目,并创建一个名为 MdSpi 的类,并继承 CThostFtdcMdSpi

{% note info %}

这里我用的是 Visual Studio 2019

注意设置好库目录,在链接器中引入静态库 thostmduserapi_se.lib

{% endnote %}

在头文件 MdSpi.h 中,我们写入如下代码:

// MdSpi.h
#include "ThostFtdcMdApi.h"
class MdSpi : public CThostFtdcMdSpi
{
public:
	// 当客户端与交易后台建立起通信连接时(还未登录前),该方法被调用。
	void OnFrontConnected();
};

在源文件 MdSpi.cpp 中,我们对 OnFrontConnected 进行重载:

// MdSpi.cpp
#include <iostream>
#include "MdSpi.h"
using namespace std;

void MdSpi::OnFrontConnected()
{
	cout << "建立网络连接成功" << endl;
}

在上面的过程中,我们做的事情很简单,新建一个名为 MdSpi 的类,并继承 CThostFtdcMdSpi;然后对 OnFrontConnected 方法进行了重载,为了简单,这里只重载了一个函数,关于其他函数,下面会陆续实现。

初始化行情接口

然后,在 CTPDemo.cppmain() 中初始化行情接口

#include <iostream>
#include "MdSpi.h"
using namespace std;
#pragma comment (lib, "thostmduserapi_se.lib")								// 链接库

CThostFtdcMdApi* g_pMdUserApi = nullptr;									// 行情指针

int main()
{
    // 初始化行情线程
    g_pMdUserApi = CThostFtdcMdApi::CreateFtdcMdApi();						// 创建行情实例
    CThostFtdcMdSpi* pMdUserSpi = new MdSpi;								// 创建行情回调实例
    g_pMdUserApi->RegisterSpi(pMdUserSpi);									// 注册事件类
    g_pMdUserApi->RegisterFront((char*)"tcp://180.168.146.187:10211");		// 设置行情前置地址
    g_pMdUserApi->Init();													// 连接运行
    // 等到线程退出
    g_pMdUserApi->Join();
    return 0;
}

在写完了上面这些代码之后,让我们回过头来看看上面的这些代码干了些什么事

#include <iostream>
#include "MdSpi.h"
using namespace std;
#pragma comment (lib, "thostmduserapi_se.lib")              // 链接库

第 1 - 4 行,我们导入了头文件 MdSpi.h,并连接了静态库 thostmduserapi_se.lib,做好准备工作。

CThostFtdcMdApi* g_pMdUserApi = nullptr;					// 行情指针
...
...
    g_pMdUserApi = CThostFtdcMdApi::CreateFtdcMdApi();		// 创建行情实例

第 6 行和第 12 行,我们创建了一个全局变量,名为 g_pMdUserApi 的指针,它的类型为 CThostFtdcMdApi,在 main 通过 CreateFtdcMdApi 创建了 Api 实例,并将其赋值给 g_pMdUserApi

CThostFtdcMdSpi* pMdUserSpi = new MdSpi;				                // 创建行情回调实例
g_pMdUserApi->RegisterSpi(pMdUserSpi);					                // 注册事件类
g_pMdUserApi->RegisterFront((char*)"tcp://180.168.146.187:10211");		// 设置行情前置地址

第 13 - 14 行,我们创建了一个 Spi 实例,并通过 RegisterSpi 将 Api 与 Spi 进行绑定;第 15 行,我们注册了前置地址以便与服务器连接。

g_pMdUserApi->Init();									        // 连接运行

第 16 行调用 Init() 函数开始正式初始化 api,也就是说前面的工作只是准备工作,到了这里 api 才真正开始工作。此时 api 会向之前注册的地址发起与 CTP 前置的连接。

接着,生成并执行 CTPDemo.exe,你就会发现黑框中出现「建立网络连接成功」字样,说明我们与行情服务器连接成功!

行情接口工作原理

行情接口的具体工作原理可以参考官方说明文档,首先可以尝试着理解,即在之后实现的过程中的流程问题

实现登录并获取行情信息

在上面,我们实现了一个最简单的 CTP 使用样例,接下来,我们在此基础上实现登录和获取行情信息

登录
登录前的准备

在进行登录操作之间,你需要在 sinmow 中注册一个模拟交易账户

SimNow 是上海期货交易所全资子公司上期技术公司专为投资者打造的期货模拟仿真交易平台,为上海期货交易所投资者教育网认证的期货模拟仿真系统。该产品仿真各交易所的交易及结算规则研发,目前已经支持国内各期货交易所的商品期货业务。

注册完成之后,请关注 investorIdbrokerId

用户登录请求

首先,让我们来看看登录时都需要提供些什么,打开 API 接口说明文档,找到函数 ReqUserLogin

ReqUserLogin 函数原型如下。我们发现,请求登录需要两个参数,一个为 CThostFtdcReqUserLoginField ,另一个为 int 类型的请求编号。

virtual int ReqUserLogin(CThostFtdcReqUserLoginField *pReqUserLoginField, int nRequestID) = 0;

现在让我们来看看 CThostFtdcReqUserLoginField 是什么东西。打开 ThostFtdcUserApiStruct.h,就在前面几行,我们很容易找到「用户登录请求」的结构体,在这里,主要截取了几个关键字段,也就是登录时我们需要提供的。

///用户登录请求
struct CThostFtdcReqUserLoginField
{
	...
	///经纪公司代码
	TThostFtdcBrokerIDType	BrokerID;
	///用户代码
	TThostFtdcUserIDType	UserID;
	///密码
	TThostFtdcPasswordType	Password;
	...
};
登录实现

MdSpi.cpp 中写入如下代码

// MdSpi.cpp
#include <iostream>
#include "MdSpi.h"
using namespace std;

extern CThostFtdcMdApi* g_pMdUserApi;				// 行情指针

void MdSpi::OnFrontConnected()
{
	cout << "建立网络连接成功" << endl;
	// 开始登录
	CThostFtdcReqUserLoginField loginReq;
	memset(&loginReq, 0, sizeof(loginReq));
	strcpy(loginReq.BrokerID, "9999");
	strcpy(loginReq.UserID, "000000");
	strcpy(loginReq.Password, "123456");
	static int requestID = 0;
	int result = g_pMdUserApi->ReqUserLogin(&loginReq, ++requestID);
	if (!result)
		cout << "发送登录请求成功" << endl;
	else
		cerr << "发送登录请求失败" << endl;
}

在之前代码的基础上,我们又添加了一些代码。

在这里,我将登录代码直接写在了 OnFrontConnected 中,那么这也就意味着,一旦建立网络连接成功,调用 OnFrontConnected 之后便会进行登录操作。

extern CThostFtdcMdApi* g_pMdUserApi;				// 行情指针

在第 6 行中,我们声明全局变量 g_pMdUserApi,也就是我们之前创建的 Api 实例

CThostFtdcReqUserLoginField loginReq;
memset(&loginReq, 0, sizeof(loginReq));
strcpy(loginReq.BrokerID, "9999");
strcpy(loginReq.UserID, "000000");
strcpy(loginReq.Password, "123456");

第 12 - 16 行,我们创建了一个 CThostFtdcReqUserLoginField 类型的对象,并写入 BrokerIDUserIDPassword注意,请替换为你自己的用户名和密码。

static int requestID = 0;
int result = g_pMdUserApi->ReqUserLogin(&loginReq, ++requestID);
if (!result)
	cout << "发送登录请求成功" << endl;
else
	cerr << "发送登录请求失败" << endl;

第 17- 22 行,将 loginReqrequestID 传入 ReqUserLogin,并输出结果。

接下来,我们尝试生成并执行 CTPDemo.exe ,发现有发送登录请求成功字样,说明发送登录请求成功。

{% note info %}

ReqUserLogin 返回值

0,代表成功。

-1,表示网络连接失败;

-2,表示未处理请求超过许可数;

-3,表示每秒发送请求数超过许可数。

{% endnote %}

登录请求响应

在发送登录请求之后,正如之前提到的,我们还需要重载回调函数 OnRspUserLogin 接收登陆信息。

MdSpi.h 中声明回调函数 OnRspUserLogin

// MdSpi.h
#include "ThostFtdcMdApi.h"
class MdSpi : public CThostFtdcMdSpi
{
public:
	// 当客户端与交易后台建立起通信连接时(还未登录前),该方法被调用。
	void OnFrontConnected();

	///登录请求响应
	void OnRspUserLogin(CThostFtdcRspUserLoginField* pRspUserLogin, CThostFtdcRspInfoField* pRspInfo, int nRequestID, bool bIsLast);
};

MdSpi.cpp 中添加如下代码

// MdSpi.cpp
void MdSpi::OnRspUserLogin(CThostFtdcRspUserLoginField* pRspUserLogin, CThostFtdcRspInfoField* pRspInfo, int nRequestID, bool bIsLast)
{
	bool bResult = pRspInfo && (pRspInfo->ErrorID != 0);
	if (!bResult) {
		cout << "账户登录成功" << endl;
		cout << "交易日: " << pRspUserLogin->TradingDay << endl;
	}
	else
		cerr << "返回错误--->>> ErrorID=" << pRspInfo->ErrorID << ", ErrorMsg=" << pRspInfo->ErrorMsg << endl;
}

在回调函数 OnRspUserLogin 中,我们首先判断了错误是否出现,若登陆成功,则打印交易日信息。

{% note info %}

你可以在说明文档中找到响应信息 pRspInfo 的结构

struct CThostFtdcRspInfoField
{
    ///错误代码
    TThostFtdcErrorIDType ErrorID;
    ///错误信息
    TThostFtdcErrorMsgType ErrorMsg;
};

{% endnote %}

最后,生成并执行 CTPDemo.exe ,可以看到如下信息,说明我们登陆成功

获取行情信息
订阅行情

在上面的操作中,我们实现了用户登陆,接下来开始尝试获取行情信息

同样的,我们在说明文档中找到 Api,SubscribeMarketData,发现他需要两个参数,一个是需要订阅的合约列表,另一个是合约数组的数量,具体的使用方法也可以在调用示例中找到。

virtual int SubscribeMarketData(char *ppInstrumentID[], int nCount) = 0;
订阅行情实现

同样的,我们需要在 MdSpi.h 中声明其回调函数

// MdSpi.h
#include "ThostFtdcMdApi.h"
class MdSpi : public CThostFtdcMdSpi
{
public:
	// 当客户端与交易后台建立起通信连接时(还未登录前),该方法被调用。
	void OnFrontConnected();

	///登录请求响应
	void OnRspUserLogin(CThostFtdcRspUserLoginField* pRspUserLogin, CThostFtdcRspInfoField* pRspInfo, int nRequestID, bool bIsLast);

	///订阅行情应答
	void OnRspSubMarketData(CThostFtdcSpecificInstrumentField* pSpecificInstrument, CThostFtdcRspInfoField* pRspInfo, int nRequestID, bool bIsLast);
};

另一方面,在 MdSpi.cpp 中的完整代码如下

// MdSpi.cpp
#include <iostream>
#include "MdSpi.h"
using namespace std;

extern CThostFtdcMdApi* g_pMdUserApi;

void MdSpi::OnFrontConnected()
{
	cout << "建立网络连接成功" << endl;
	// 开始登录
	CThostFtdcReqUserLoginField loginReq;
	memset(&loginReq, 0, sizeof(loginReq));
	strcpy(loginReq.BrokerID, "9999");
	strcpy(loginReq.UserID, "000000");
	strcpy(loginReq.Password, "123456");
	static int requestID = 0;
	int result = g_pMdUserApi->ReqUserLogin(&loginReq, ++requestID);
	if (!result)
		cout << "发送登录请求成功" << endl;
	else
		cerr << "发送登录请求失败" << endl;
}

void MdSpi::OnRspUserLogin(CThostFtdcRspUserLoginField* pRspUserLogin, CThostFtdcRspInfoField* pRspInfo, int nRequestID, bool bIsLast)
{
	bool bResult = pRspInfo && (pRspInfo->ErrorID != 0);
	if (!bResult) {
		cout << "账户登录成功" << endl;
		cout << "交易日: " << pRspUserLogin->TradingDay << endl;
		// 开始订阅行情												 // 新增
		char** ppInstrumentID = new char* [50];						// 新增
		ppInstrumentID[0] = "cu2108";								// 新增
		g_pMdUserApi->SubscribeMarketData(ppInstrumentID, 1);		// 新增
	}
	else
		cerr << "返回错误--->>> ErrorID=" << pRspInfo->ErrorID << ", ErrorMsg=" << pRspInfo->ErrorMsg << endl;
}

void MdSpi::OnRspSubMarketData(CThostFtdcSpecificInstrumentField* pSpecificInstrument, CThostFtdcRspInfoField* pRspInfo, int nRequestID, bool bIsLast)
{
	bool bResult = pRspInfo && (pRspInfo->ErrorID != 0);
	if (!bResult) {
		cout << "订阅行情成功" << endl;
		cout << "合约代码: " << pSpecificInstrument->InstrumentID << endl;
	}
	else
		cerr << "返回错误--->>> ErrorID=" << pRspInfo->ErrorID << ", ErrorMsg=" << pRspInfo->ErrorMsg << endl;
}

第 32 - 35 行,我们在登陆的回调函数 OnRspUserLogin 中,添加了订阅行情请求,指定合约 cu2108

第 41 - 51 行,我们添加了回调函数 OnRspSubMarketData,返回订阅行情的响应信息。

生成并执行 CTPDemo.exe ,可以看到如下信息,说明我们订阅行情成功

获取深度行情

正如我们在 行情接口工作原理 中看到的,订阅合约之后,我们还需要持续接收合约的具体行情信息

在说明文档中找到对应的回调函数 OnRtnDepthMarketData

virtual void OnRtnDepthMarketData(CThostFtdcDepthMarketDataField *pDepthMarketData) {};

MdSpi.h 中对其进行声明

// MdSpi.h
#include "ThostFtdcMdApi.h"
class MdSpi : public CThostFtdcMdSpi
{
public:
	// 当客户端与交易后台建立起通信连接时(还未登录前),该方法被调用。
	void OnFrontConnected();

	///登录请求响应
	void OnRspUserLogin(CThostFtdcRspUserLoginField* pRspUserLogin, CThostFtdcRspInfoField* pRspInfo, int nRequestID, bool bIsLast);

	///订阅行情应答
	void OnRspSubMarketData(CThostFtdcSpecificInstrumentField* pSpecificInstrument, CThostFtdcRspInfoField* pRspInfo, int nRequestID, bool bIsLast);

	///深度行情通知
	void OnRtnDepthMarketData(CThostFtdcDepthMarketDataField* pDepthMarketData);
};

MdSpi.cpp 中实现 OnRtnDepthMarketData

void MdSpi::OnRtnDepthMarketData(CThostFtdcDepthMarketDataField* pDepthMarketData)
{
	cout << "=====获得深度行情成功====="  << endl;
	cout << "合约代码:" << pDepthMarketData->InstrumentID << endl;
	cout << "最新价:" << pDepthMarketData->LastPrice << endl;
	cout << "成交量:" << pDepthMarketData->Volume << endl;
}

最后,生成并执行 CTPDemo.exe ,可以不断看到最新的行情信息。

至此,我们简单实现了「行情部分」的「登录」和「获取行情」,总的来说,重点是理解 CTP 的工作原理以及流程。对于「交易部分」的实现也是类似的,这里就不再赘述。

你可以在这里找到完整的代码:https://github.com/EmoryHuang/CTPDemo

参考资料

  • CTP 客户端开发指南
  • API 接口说明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CTP 学习笔记 的相关文章

  • 为什么我不能用 `= delete;` 声明纯虚函数?

    Intro 纯虚函数使用通用语法声明 virtual f 0 然而 自 c 11 以来 有一种方法可以显式地传达non existence 特殊 成员函数的 Mystruct delete eg default constructor Q
  • 如何创建可以像 UserControl 一样编辑的 TabPage 子类?

    我想创建一个包含一些控件的 TabPage 子类 并且我想通过设计器来控制这些控件的布局和属性 但是 如果我在设计器中打开子类 我将无法像在 UserControl 上那样定位它们 我不想创建一个带有 UserControl 实例的 Tab
  • 为什么要序列化对象需要 Serialized 属性

    根据我的理解 SerializedAttribute 不提供编译时检查 因为它都是在运行时完成的 如果是这样 那么为什么需要将类标记为可序列化呢 难道序列化器不能尝试序列化一个对象然后失败吗 这不就是它现在所做的吗 当某些东西被标记时 它会
  • C++:重写已弃用的虚拟方法时出现弃用警告

    我有一个纯虚拟类 它有一个纯虚拟方法 应该是const 但不幸的是不是 该接口位于库中 并且该类由单独项目中的其他几个类继承 我正在尝试使用这个方法const不会破坏兼容性 至少在一段时间内 但我找不到在非常量方法重载时产生警告的方法 以下
  • POCO HTTPSClientSession 发送请求时遇到问题 - 证书验证失败

    我正在尝试使用 POCO 库编写一个向服务器发出 HTTPS 请求的程序 出于测试目的 我正在连接到具有自签名证书的服务器 并且我希望允许客户端进行连接 为了允许这种情况发生 我尝试安装InvalidCertificateHandler这是
  • 访问者和模板化虚拟方法

    在一个典型的实现中Visitor模式 该类必须考虑基类的所有变体 后代 在许多情况下 访问者中的相同方法内容应用于不同的方法 在这种情况下 模板化的虚拟方法是理想的选择 但目前这是不允许的 那么 模板化方法可以用来解析父类的虚方法吗 鉴于
  • IronPython:没有名为 json 的模块

    我安装了 IronPython 我的 python 文件如下所示 import sys print sys version import json 运行它的代码 var p Python CreateEngine var scope p C
  • 如何识别 WPF 文本框中的 ValidationError 工具提示位置

    我添加了一个箭头来指示工具提示中的文本框 当文本框远离屏幕边缘时 这非常有效 但是当它靠近屏幕边缘时 工具提示位置发生变化 箭头显示在左侧 Here is the Image Correct as expected since TextBo
  • 在 2D 中将一个点旋转另一个点

    我想知道当一个点相对于另一个点旋转一定角度时如何计算出新的坐标 我有一个块箭头 想要将其相对于箭头底部中间的点旋转角度 theta 这是允许我在两个屏幕控件之间绘制多边形所必需的 我无法使用和旋转图像 从我到目前为止所考虑的情况来看 使问题
  • Qt 创建布局并动态添加小部件到布局

    我正在尝试在 MainWindow 类中动态创建布局 我有四个框架 它们是用网格布局对象放置的 每个框架都包含一个自定义的 ClockWidget 我希望 ClockWidget 对象在调整主窗口大小时相应地调整大小 因此我需要将它们添加到
  • 将数据打印到文件

    我已经超载了 lt lt 运算符 使其写入文件并写入控制台 我已经为同一个函数创建了 8 个线程 并且我想输出 hello hi 如果我在无限循环中运行这个线程例程 文件中的o p是 hello hi hello hi hello hi e
  • 在非活动联合成员上使用“std::addressof”是否定义明确[重复]

    这个问题在这里已经有答案了 下面的代码是尝试实现constexpr的版本offsetof在 C 11 中 它可以在 gcc 7 2 0 和 clang 5 0 0 中编译 这取决于申请std addressof工会非活跃成员的成员 这是明确
  • 为什么我不应该对不是由 malloc() 分配的变量调用 free() ?

    我在某处读到 使用它是灾难性的free删除不是通过调用创建的对象malloc 这是真的 为什么 这是未定义的行为 永远不要尝试它 让我们看看当您尝试时会发生什么free 自动变量 堆管理器必须推断出如何获取内存块的所有权 为此 它要么必须使
  • 如何一步步遍历目录树?

    我发现了很多关于遍历目录树的示例 但我需要一些不同的东西 我需要一个带有某种方法的类 每次调用都会从目录返回一个文件 并逐渐遍历目录树 请问我该怎么做 我正在使用函数 FindFirstFile FindNextFile 和 FindClo
  • System.Runtime.InteropServices.COMException(0x80040154):[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 我在 C 项目中遇到异常 System Runtime InteropServices COMException 0x80040154 检
  • 在类的所有方法之前运行一个方法

    在 C 3 或 4 中可以做到这一点吗 也许有一些反思 class Magic RunBeforeAll public void BaseMethod runs BaseMethod before being executed public
  • 什么是 __declspec 以及何时需要使用它?

    我见过这样的例子 declspec在我正在阅读的代码中 它是什么 我什么时候需要使用这个构造 这是 Microsoft 对 C 语言的特定扩展 它允许您使用存储类信息来赋予类型或函数属性 文档 declspec C https learn
  • 用于 C# XNA 的 Javascript(或类似)游戏脚本

    最近我准备用 XNA C 开发另一个游戏 上次我在 XNA C 中开发游戏时 遇到了必须向游戏中添加地图和可自定义数据的问题 每次我想添加新内容或更改游戏角色的某些值或其他内容时 我都必须重建整个游戏或其他内容 这可能需要相当长的时间 有没
  • 带重定向标准流的 C# + telnet 进程立即退出

    我正在尝试用 C 做一个 脚本化 telnet 项目 有点类似于Tcl期望 http expect nist gov 我需要为其启动 telnet 进程并重定向 和处理 其 stdin stdout 流 问题是 生成的 telnet 进程在
  • 是否可以在 C# 中强制接口实现为虚拟?

    我今天遇到了一个问题 试图重写尚未声明为虚拟的接口方法的实现 在这种情况下 我无法更改接口或基本实现 而必须尝试其他方法 但我想知道是否有一种方法可以强制类使用虚拟方法实现接口 Example interface IBuilder

随机推荐

  • ThreadLocal学习

    1 threadLocal图解 java lang ThreadLocal类实现了线程的本地存储 ThreadLocal的内部实现 ThreadLocal的内部实现包括一个类似HashMap的对象 这里称之ThreadLocalMap Th
  • 华为OD机试 - 表达式括号匹配(Java)

    题目描述 1 2 3 3 8 0 1 2 这是一个简单的数学表达式 今天不是计算它的值 而是比较它的括号匹配是否正确 前面这个式子可以简化为 这样的括号我们认为它是匹配正确的 而 这样的我们就说他是错误的 注意括号里面的表达式可能是错的 也
  • rhel的一些配置

    ip etc sysconfig network scripts ifcfg eth0 DEVICE eth0 NM CONTROLLED yes ONBOOT yes BOOTPROTO dhcp BOOTPROTO static IPA
  • 消息转换器统一对null值处理

    import java nio charset Charset import java util ArrayList import java util List import org springframework context anno
  • STM32 CAN通信理解(是半双工还是全双工?)

    STM32F429 CAN通信 CAN 是控制器局域网络 Controller Area Network 的简称 它是由研发和生产汽车电子产品著称的德国 BOSCH 公司开发的 并最终成为国际标准 ISO11519 是国际上应用最广泛的现场
  • 数据结构--二叉树的二叉链表实现

    1 二叉树的二叉链表示意图 二叉链表的每个结点由三个域组成 数据域 左指针域和右指针域 左右指针分别用来保存左右孩子结点的存储地址 2 二叉链表实现二叉树 2 1 头文件及其定义 BTNode h pragma once typedef c
  • 利用umi实现前端自定义打包

    背景 目前项目有这么一个需求 一套前端代码里面有两个项目 这两个项目一个是设备页面 一个是平台页面 因为这两个项目有共同的页面和代码 所有就写在一个仓库里面 出现的问题是 打包项目A并且也会随带的项目B的代码 这就会造成打包体积的增大 打包
  • java bean的使用

    这是我最终尝试成功的项目页面截图 网页效果 注意的点 1 Dog类 bean类 必须拥有一个无参构造器 这和spring里的bean的设计是一个理念 spring借鉴于javabean 2 通过getXxx和setXxx方法来访问成员变量
  • SpringBoot系列(五):SpringBoot整合Mybatis实现多表关联查询

    摘要 本文我们将继续分享介绍Spring Boot在整合Mybatis开发企业级应用时其他典型的业务场景 即Mybatis是如何实现多表关联查询时将查询结果集与对象进行映射的 主要的内容包含 一对一的表关联 和 一对多 多对多的表关联 查询
  • python语言合法命名-Python中的标识符命名方法及使用注意事项

    Python中的标识符是由用户定义的名称 用来指定变量名称 函数名称 类名称 模块等名称 1 标识符的命名规则 1 不能使用Python的保留字 关键字 来命名标识符 2 Python标识符合法的字符包括 26个大小写字母 a z A Z
  • Linux中select poll和epoll的区别

    select的本质是采用32个整数的32位 即32 32 1024来标识 fd值为1 1024 当fd的值超过1024限制时 就必须修改FD SETSIZE的大小 这个时候就可以标识32 max值范围的fd 对于单进程多线程 每个线程处理多
  • STM32笔记:使用SysTick测量程序的运行时间

    本实验用来测量延时函数 如下 的运行时间 void Delay IO u32 nCount for nCount 0 nCount SysTick c include SysTick h IO u32 TimingDelay 0 void
  • 自己的第一个程序 — QT聊天软件 小感

    其实当初师兄给我这个任务的时候还是在2010年的11月份 那个时候刚到项目上的我超级自信 总认为只要自己想做 稍微看下书 动下手就可以写出来了 所以当师兄问我多久可以做好的时候 我开始满怀信心的说一个星期 后来细想下有点悬 说还是2个星期吧
  • VUE props 实现原理(源码解析)

    VUE props 实现原理 源码解析 前言 我们在使用组件的时候 都会给组件传入一些属性 但是在使用时 却只是关注了它传递数据的功能 没有想过它是怎样的一个原理 具体是怎么实现的 其实我们平时写出来的组件 本质上就是一个template模
  • 屏蔽SSH服务的弱密码算法

    前言 等保测试 1 目标主机SSH服务存在RC4 CBC或None弱加密算法 2 如果配置为CBC模式的话 SSH没有正确地处理分组密码算法加密的SSH会话中所出现的错误 解决办法 仅保留CTR加密算法 参考文章 1 编辑 ssh 配置文件
  • Windows 域基础环境搭建-Win_Server_2003

    文章目录 1 Windows 域基础环境搭建 Win Server 2003 1 1 Windows域简介 1 1 1 Windows域作用 1 1 2 Windows计算机查看所在域方式 1 1 3 域控 DC 1 2 Windows域环
  • 如何用Selenium做自动化测试

    我们做自动化测试如果想提高一下自己的软实力 我建议还是学着多写写脚本 这样能锻炼自己的编程能力 让我们以开放测试的思维去测试产品 而不是为了测试而测试 那样我觉得没什么意义 当然这样不是说其他不写脚本 简单的自动化没意义 本人不反对 认真对
  • 【AI之路】使用huggingface_hub优雅解决huggingface大模型下载问题

    文章目录 前言 一 Hugging face是什么 二 准备工作 三 下载整个仓库或单个大模型文件 1 下载整个仓库 2 下载单个大模型文件 总结 附录 前言 Hugging face 资源很不错 可是国内下载速度很慢 动则GB的大模型 下
  • C/C++ algorithm头文件的max()、min()、abs()和math头文件的fabs()

    对于一般的数据类型 整型和浮点型 我们经常要得到最大值 最小值和绝对值 本文章利用一些头文件的函数 进行展示 函数的参数及使用在程序代码及注释中体现 代码如下 Project 最值及绝对值 Date 2018 07 31 Author Fr
  • CTP 学习笔记

    CTP 学习笔记 前言 综合交易平台 Comprehensive Transaction Platform CTP 是专门为期货公司开发的一套期货经纪业务管理系统 由交易 风险控制和结算三大系统组成 前后研究了两个多星期 CTP 各种查资料