C++使用纯虚函数的接口封装以及接口的调用方法

【C++】自定义函数的封装和调用 在做高翔《一起做RGB-D SLAM》系列过程中,由于工程较大,需要将自己写的函数进行封装,方便调用,将函数的封装和调用过程记录下来,方便查阅 函数的封装 将需要封装的函数的声明和定义分别放在头文件(.h)和源文件(.cpp)中 头文件:包含了库的引用,数据类型的定义,结构体定义,函数声明 源文件:函数定义 以特征点检测和匹配的函数为例 feature_detect.h #prag... 阅读详情

C++使用纯虚函数的接口封装以及接口的调用方法

受疫情的影响,本研狗最近一直在家养老,但是,实验室那边的项目突然压下来了,无奈,只好云办公呗。给的任务是对我之前完成的项目做一个封装,对外部提供接口使用。这**就涉及到我知识的盲区了呀,时间还催的挺紧,只好这两天边学边做,终于肝出来了一个初步的成果,做好了空的原型库定义,加上了要求的输入输出参数,封装成了接口。然后想着趁着热乎劲,赶紧把方法总结下来,遂发布了这篇文章,供大家参考,也给以后自己再写类似接口的时候当参考。

首先说一下为什么要用到纯虚函数,主要是因为要向用户提供接口,又不希望暴露太多的信息,于是在对外部发布的头文件里定义的方法都是纯虚的,也不包含私有方法和属性,而其具体实现以及成员变量由内部封装的库来实现。简单来说,就是要发布的外部文件定义抽象类当作父类,其内部子类继承父类,提供具体实现。

下面我整了一个很简单的小例子来说明这个是如何实现的,例子是调用opencv实现图片的水平翻转和灰度化操作。

小例子的代码均已上传到github:传送门

库的封装

首先,是对外发布的接口文件image_processing.h,如下所示。

// ------------------image_processing.h------------------
// 这个是对外发布的接口,这个文件里只包含对外提供的方法,不会包含私有方法和属性

#pragma once

#include <opencv2/opencv.hpp>

// 注:如果用到了.def来配置生成dll,则可以用下面被注释掉的代码
//#ifdef _EXPORTING
//#define CLASS_DECLSPEC __declspec(dllexport)
//#else
//#define CLASS_DECLSPEC __declspec(dllimport)
//#endif

#define CLASS_DECLSPEC __declspec(dllexport)  // 表示这里要把类导出

// 基类
class Processing  // 这个类是对外发布的,所有方法均是纯虚的,不会暴露实现
{
public:
	// 提供接口框架的纯虚函数
	virtual void imageProcessing(cv::Mat image) = 0;  // 图像处理

	virtual cv::Mat getTheResult() = 0;  // 输出成员变量

// 这里最好不要出现私有成员变量,因为只是接口作用,不暴露实现,成员变量可以在具体实现头文件定义
};

可以看到,这个类里面只有一个成员函数,virtual的声明表示这个函数是纯虚的,还有后面的 = 0也要加上,告诉编译器这是个纯虚函数。

细心的童鞋可能就发现了,上面注释了一大段#define这些东西是什么鬼?解释一下,这个跟一个.def的配置文件有关,这个配置文件一般用于生成dll,也就是我们现在正在做的事情。这段注释意思是如果.def中define _EXPRORTING,就用__declspec(dllexport)来替代CLASS_DECLSPEC;如果没有define,就用__declspec(dllimport)来替代CLASS_DECLSPEC。

关于__declspec(dllexport)这个东西,它表示的是声明一个导出函数(或者导出类),是说这个函数(类)要从本DLL导出,表示要给别人用,一般用于生成dll。而__declspec(dllimport)是声明一个导入函数(或者导入类),是说这个函数(类)是从别的DLL导入,一般用于使用某个dll的exe中。

解释清楚了,再回过头看我为啥没有用这段注释的代码,因为这段代码是跟.def有关,我没有用.def,而是手动在头文件定义了这个就是输出。当然,在后面这个头文件给用户用的时候还是要再手动改回输入的。

然后,是具体实现的头文件,可以看到这里我对这两个派生类定义时用到了CLASS_DECLSPEC,结合前面的头文件,其实是用__declspec(dllimport)来定义的这个类,表示这两个类要导出。

// ----------------image_processing_impl.h----------------
// 实现文件,负责接口文件的具体实现,不对外发布

#pragma once

#include "image_processing.h"

// 派生类之灰度化操作
class CLASS_DECLSPEC ToGray : public Processing
{
public:
	void imageProcessing(cv::Mat image);

	cv::Mat getTheResult();

private:
	cv::Mat result_image;
};

// 派生类之水平翻转操作
class CLASS_DECLSPEC Flip : public Processing
{
public:
	void imageProcessing(cv::Mat image);

	cv::Mat getTheResult();

private:
	cv::Mat result_image;
};

然后是具体实现cpp文件。

// ----------------image_processing_impl.cpp----------------
// 实现文件,负责接口文件的具体实现,不对外发布

#include "image_processing_impl.h"

void ToGray::imageProcessing(cv::Mat image)
{
	cv::cvtColor(image, result_image, CV_RGB2GRAY);
}

cv::Mat ToGray::getTheResult()
{
	return result_image;
}

void Flip::imageProcessing(cv::Mat image)
{
	cv::flip(image, result_image, 1);
}

cv::Mat Flip::getTheResult()
{
	return result_image;
}

上述就是整个的封装,但是到这里还没有结束,因为用户接口头文件里定义的是抽象类,而抽象类并不能实例化,因此要提供用户创造对象的方式,还需对外发布一个用来创造实例的头文件,以及内部对应的cpp文件(当然内部也要有头文件)。

// -----------image_processing_factory.h-----------
// 创造实例头文件,对外发布

#pragma once

#include "image_processing.h"

class CLASS_DECLSPEC ToGrayFactory  // 根据image_processing.h 定义这个是导入还是导出(外部导入,内部导出),下同理
{
public:
	Processing* create();  // 提供一个创建对象的方法   
};

class CLASS_DECLSPEC FlipFactory
{
public:
	Processing* create();  // 提供一个创建对象的方法   
};
// -----------image_processing_factory.cpp-----------
// 创造实例头文件

#include "image_processing_factory.h"
#include "image_processing_impl.h"

Processing* ToGrayFactory::create()
{
	return new ToGray;
}

Processing* FlipFactory::create()
{
	return new Flip;
}

好了,到这里封装工作就可以完成了,把这五个文件编译出dll和lib供外部调用即可。

封装库的使用

前面说了内部如何进行封装,下面说一下外部也就是用户这边如何使用。首先,必然需要前面文件编译出来的静态链接库.lib和动态链接库.dll,把这两个库文件拷贝到工程目录下。然后,还需要两个前面封装库用的两个头文件,作为接口文件。头文件 image_processing.h 定义了纯虚函数,内部有子类继承提供具体实现,供外部调用;头文件 image_processing_factory.h 提供把封装库的子类实例化来创造对象的方法。因此也要把这两个头文件拷贝至工程目录下。

总结一下,外部要调用库的话,需要编译出来的静态链接库.lib和动态链接库.dll,还有两个头文件 image_processing.h 和 image_processing_factory.h ,然后写个main函数调用即可。

但有一点要注意,外部调用的头文件image_processing.h要改动一下,把

#define CLASS_DECLSPEC __declspec(dllexport)  // 表示这里要把类导出

改为

#define CLASS_DECLSPEC __declspec(dllimport)  // 表示这里要把类导入

包含主函数的cpp如下所示。

// ----------demo.cpp----------
// 调用封装库实现对图片的操作
#include <opencv2/opencv.hpp>
#include "image_processing_factory.h"

int main()
{
	ToGrayFactory factory_gray;  // 用户接口
	FlipFactory factory_flip;  // 用户接口
	Processing* processor_gray = factory_gray.create();  //创建灰度操作对象
	Processing* processor_flip = factory_flip.create();  //创建翻转操作对象

	cv::Mat orig_image = cv::imread("D:/test_image.jpg");
	cv::imshow("orig_image", orig_image);

	processor_gray->imageProcessing(orig_image);
	cv::imshow("gray_image", processor_gray->getTheResult());

	processor_flip->imageProcessing(orig_image);
	cv::imshow("flip_image", processor_flip->getTheResult());

	cv::waitKey(0);

	return 0;
}

执行结果

在这里插入图片描述

WIN10DLL的封装——vs2019(类的封装和调用) 本文主要参考了这篇博文 VS2019 C++接口类的封装与调用DLL+嵌套类+BP气温预测_我不会起名字的博客-优快云博客 本文介绍了vs封装类的dll的方法,经过本人实测可以跑通 PS:之前的博文提到的问题“这两种方法都是 dll 封装函数的,没有找到 dll封装类的;需要封装类的场景很少吗,求问?” 结合本文正好可以作为对比。 ——————————————————————————————————————————— PS2:C++接口封装及DLL中使用头文件实例_一行一生-优快云博客_c. 阅读详情

相关推荐

虚函数和纯虚函数的区别

多态 多态性是将接口与实现进行分离;用形象的语言来解释就是实现以 共同的方法,但因个体差异,而采用不同的策略。 封装wrap:实现细节隐藏,使得代码模块化。把成员数据和成员函数封 装起来,通过公共的成员接口进行成员数据的操作。 继承inheritance:扩展已存在的代码,目的是为了代码重用 多态polymorphism:目的是为了接口重用。也就是说,不论传递过来的 究竟是那个类的对象,函数都能够通过同一个接口 调用到适应各自对象的实现方法。 用virtual修饰的成员函

hsjdjhdijdsj的博客 4655

C++的纯虚函数使用与接口实现

C++的纯虚函数使用与接口实现

高空的博客 2816

Cherno C++ P28 C++接口(纯虚函数)

Cherno C++学习笔记

weixin_44452626的博客 1316

C++代码接口封装两种方法

C++代码接口封装方法方法1:对原功能代码再包一层方法2:父类声明为纯虚函数博客参考 概述 在多人协作开发中,常常会涉及代码打包和接口封装。 方法1:对原功能代码再包一层 由于我们不想暴露AImpl.h中,故对其封装,即在A.h和A.cpp中对其包一层。 我们甚至在A.h中都不需要声明 AImpl *imp_的,在A.h中可以只声明一个void *imp_,在A.cpp中将该void *指针转换成Almpl *即可。 AImpl.h class AImpl { public: ... priva

嵌入式与人工智能 5537

动态链接库_导出类

本人经过两天的研究,终于掌握了动态链接库的使用方法,动态链接库的使用上并不难,难在动态链接库中的导出类, 把dll中类的成员函数导出来才是最重要的,我就用几个例子介绍一下几种导出的方法: 第一种: 这种方法不是很好,实现的也比较麻烦,可以参考 例子中的1_DLLDemoCll工程 原理:利用虚函数导出,导出的dll中申请的对象指针,再利用虚函数的特性,利用虚函数表实现了类函数的导出,比较麻烦 建议了解一下。 第二种: 这种方法实现起来比较简单,采用的是静态加载的方式,但是有很多的局限性,因为当dll有错是整个应用都会出问题,不建 议在打的工程中使用。 具体代码实现见static_link_DLL工程,其中dll文件由Abstrct文件编译。 第三种: 这种方法是我推荐的,它是由动态加载进来的,其中实现原理是:运用抽象类,为工程创建接口,通过接口来导出类成员函数 这种方式通用性强,而且代码比较的健壮,节省内存。见工程useDLL。

《UE4游戏开发》之 《查cook资源失败,问题记录》

当项目打包时,cook出错,但是从原始引擎提供的信息中,没有提示具体的那个资源出错,要查到出错资源,还是非常麻烦。 下面就材质cook出错,增加一些日志,方便查找问题做简单记录 材质cook的一个阶段,是收集MaterialShaderMap,如果在这个阶段收集的信息出错,将会在FShaderMapContent::GetOutdatedTypes函数中,因为Shaders指针指向的内存有问题而出错,通过断点发现,FShaderMapContent这个类对象指针为null,而这个类对象的来源于AllMat.

qq_21919621的博客 2580

C++纯虚函数和抽象类

C++纯虚函数和抽象类

月山知了的博客 288

ollama服务暴露公网?三步搞定Nginx反向代理配置(避坑指南)

本文详细介绍了如何通过Nginx反向代理安全地将Ollama服务暴露到公网,避免直接修改OLLAMA_HOST带来的安全隐患。文章提供了从SSL证书部署到高级安全加固的全流程实战指南,包括IP白名单、基础认证、速率限制等企业级安全措施,帮助用户实现安全可靠的外部访问。

wood5的博客 177

c++多态

然后通 过基类的指针来访问 Dog 类对象与 Cat 类的对象的 run()方法,前提是基类的 run()方法必须声 明为虚函数,如果不声明为虚函数,基类的指针将访问到基类自己的 run()方法。是一种把数据和操作数据的函数捆绑在一起的机制,数据抽象是一种仅向用户暴 露接口而把具体的实现细节隐藏起来的机制,C++ 通过创建类来支持封装和数据隐藏(public、 protected、private)。包含纯虚函数的类是抽象类,抽象类不能定义实例,但可以声明指向实现该抽象类的具体类的 指针或引用。

m0_69211839的博客 574

SAP 资产主数据屏幕增强:对比字段追加与子屏幕/页签 2 种方案优劣

本文深入对比了SAP资产主数据(AS01/AS03)屏幕增强的两种方案:字段追加与子屏幕/页签。从技术实现、业务适配、系统维护等维度分析各自优劣,提供方案选型决策树和混合方案实践建议,帮助企业在SAP实施中做出最优选择。

weixin_28559803的博客 285

Jev模型入门:从API接入到本地部署的完整实践指南

大模型的应用方式正从单一聊天窗口转向复杂的自动化工作流,而推理模型因具备指令跟随与结构化输出能力,成为构建Agent和数据处理管线的关键组件。Jev作为面向通用场景的推理模型,不仅提供了OpenAI兼容的API接口,还支持本地量化部署,兼顾隐私、可控性与长期成本。本文从模型定位出发,明确Jev与基座模型的差异,随后介绍密钥管理、API调用、参数调优,并深入演示基于Ollama与llama.cpp的本地部署流程,包括GGUF模型文件与chat template的匹配要点。针对实践中的常见问题,如密钥泄露、ma

weixin_29057237的博客 359

c++成员声明中的非法限定名_C++中的封装、继承、多态

封装(encapsulation):就是将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成”类”,其中数据和函数都是类的成员。封装的目的是增强安全性和简化编程,使用者不必了解具体的实现细节,而只是要通过外部接口,特定的访问权限来使用类的成员。封装可以隐藏实现细节,使得代码模块化。继承(inheritance):C++通过类派生机制来支持继...

weixin_42362491的博客 1258

Java + Selenium + TestNg + Allure Web UI 自动化

我们知道,在Python中针对Web UI自动化有Python + Selenium + Pytest/Unittest +Allure的技术栈。那么对于Java而言,也有其独特的Web UI 自动化技术栈。相同的,我们同样的运用Selenium框架,测试报告同样运用Allure。不同的是,我们运用的语言由Python转化为了Java,测试用例管理框架从Pytest转化为了TestNG。

qq_45594960的博客 1913

C++ 虚函数/纯虚函数/抽象类/接口

虚函数 定义一个函数为虚函数,不代表函数为不被实现的函数;定义他为虚函数是为了允许用基类的指针来调用子类的这个函数。 class A { public: virtual void foo() { cout<<"A::foo() is called"<<endl; } }; class B:public A { public: void foo() { cout<<"B::foo() is called"<<endl; } }; int ma

zzwwgg1984的博客 3420

Frida三层次钩子快速逆向VMP+OLLVM加固APK

VMP(虚拟机保护)和OLLVM(控制流平坦化)是Android应用常见的双重混淆技术,其核心原理是将Java逻辑虚拟化、Native代码控制流打散,从而阻断传统静态分析与简单Hook。但任何混淆都必须在CPU上执行,因此必然暴露JNI注册行为、ABI参数传递规律及系统调用痕迹。基于此,Frida可通过跨层联动——从JNI_OnLoad捕获Native库加载、在VMP解释器入口截获字节码上下文、于关键系统调用(如read/gettimeofday)反向追踪数据流——构建可观测、可复现的动态分析链。该方法无需

weixin_30888413的博客 1002

别再到处找DEM了!手把手教你免费下载90米、30米、12.5米全球高程数据(附国内网盘链接)

本文详细介绍了如何免费获取全球90米(SRTM3)、30米(ASTER GDEM)和12.5米(ALOS)高精度DEM数据,并提供了国内网盘链接以绕过国际平台访问障碍。通过对比不同数据源的特点和适用场景,帮助用户根据项目需求选择最合适的DEM数据,同时分享了数据处理和质量提升的实用技巧,实现从数据获取到应用的无缝衔接。

weixin_28725949的博客 671

【C++】深入理解C++虚函数与纯虚函数

虚函数是在基类中使用关键字virtual声明的成员函数,它允许派生类对其进行重写(Override),实现运行时多态。当通过基类指针或引用调用虚函数时,实际调用的是对象类型对应的派生类中的函数,这个过程称为动态绑定(Dynamic Binding)或晚绑定(Late Binding)。虚函数重写指的是派生类中提供一个函数版本,该版本与基类中具有相同名称、相同返回类型和相同参数列表的虚函数相匹配。通过这种方式,派生类可以提供自己特定的实现,替换或扩展基类的行为。

Colorful___的博客 2万+

YOLOv13改进策略【损失函数篇】| 2024 引进Focaler-IoU损失函数 加强边界框回归 (Focaler-DIoU、Focaler-GIoU、Focaler-CIoU)

本文记录的是改进YOLOv13的损失函数,将其替换成。现有研究通过利用边界框之间的几何关系来提高回归性能,但忽略了困难样本和简单样本分布对边界框回归的影响。不同检测任务中困难样本和简单样本的分布不同,对于简单样本占主导的检测任务,关注简单样本的边界框回归有助于提高检测性能;对于困难样本比例较高的检测任务,需要关注困难样本的边界框回归。,提高检测器在不同检测任务中的性能。实现的。

Limiiiing的博客 870

【C++实践】C++代码接口封装 — “父类声明纯虚函数”法

目录1、实现父类声明纯虚函数法 2、打包生成静态库.a,供开发者调用在代码的开发过程中,会遇到接口封装的问题,而把父类声明纯虚函数就是封装的一种方式;下面通过居体实例来了解接口封装:例如:封装两个接口函数,Add函数实现两个数相加,Sub实现两个数相减;首先定义父类纯虚函数接口:COperator.h COperator.cpp 子类继承父类实现虚拟接口方法: baseoperator.h baseoperator.cpp main.cpp 测试结果: 生成静态库如上。你只需

Fanjufei的博客 1558

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

c++的接口,纯虚函数,和抽象类

C++纯虚函数 一、定义 纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加"=0" virtual void funtion1()=0; 二、引入原因 1、为了方便使用多态特性,我们常常需要在基类中定义虚函数。 2、在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。 为了解决上述问题,引入了纯虚函数的概念,将函数定义为纯虚函数(方..

xinlongkun的博客 772

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

C++接口定义及实现举例

原地址:https://www.cnblogs.com/xiaocaocao/p/8451838.html 一、接口的定义 有时候,我们得提供一些接口给别人使用。接口的作用,就是提供一个与其他系统交互的方法。其他系统无需了解你内部细节,并且也无法了解内部细节,只能通过你提供给外部的接口来与你进行通信。根据c++的特点,我们可以采用纯虚函数的方式来实现。这样做的好处是能够实现封装和多态。...

wang13342322203的博客 3582

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

C++ 纯虚函数的简单实现

纯虚函数的简单实现 纯虚函数是一个在基类中说明的虚函数,在基类中没有定义,要求任何派生类都定义自己的版本 纯虚函数为个派生类提供一个公共界面(接口的封装和设计、软件的模块功能划分) 含有纯虚函数的类,称为抽象基类,不可实列化。即不能创建对象,存在的意义就是被继承,提供族类的公共接口。 纯虚函数只有声明,没有实现,被“初始化”为0。 如果一个类中声明了纯虚函数,而在派生类中没有对该函数定义,则该...

Ai_King的博客 4272

一种C++ DLL接口封装的基本方法

一种C++ DLL接口封装的基本方法                在产品开发过程中,我们经常会需要对外提供DLL的接口,这种接口可以在产品开是设计好的,也可能是在基本功能开发完成进行封装的,这里将介绍一种比较基本常用的封装方法,供初学者参考。欢迎提出好的改进方式。 先提几点要求,包含我们的目标、要求和本文范围等, 1、对外提供的文件清晰,只包含必需的头文件、DLL文件及LIB文件 2、

giswangbaogang126的专栏 4057

【C++杂记】C++代码接口封装方法

概述 在多人协作开发中,常常会涉及代码打包和接口封装。 方法1:对原功能代码再包一层 由于我们不想暴露AImpl.h中,故对其封装,即在A.h和A.cpp中对其包一层。 我们甚至在A.h中都不需要声明 AImpl *imp_的,在A.h中可以只声明一个void *imp_,在A.cpp中将该void *指针转换成Almpl *即可。 AImpl.h [1] class AImpl...

春播秋忙 6784

c++ 不允许使用抽象类类型的对象_C++学习大纲:纯虚函数和抽象类

C++ 纯虚函数和抽象类1. 纯虚函数定义格式:在函数原型后面加上符号 “=0”class A{...public:virtual int f()=0;}2. 抽象类包含纯虚函数的类成为抽象类,用于为派生类提供一个基本框架和一个公共的对外接口,派生类(或派生类的派生类...),应对抽象基类的所有纯虚成员函数进行实现。(形式上跟Java的抽象类有点像,作用跟Java的接口有点像)——————————...

weixin_39824801的博客 1万+

C++接口的定义与实现的详细过程

原地址:https://www.cnblogs.com/KunLunSu/p/6443882.html 1.接口的定义与实现 所谓的接口,即将内部实现细节封装起来,外部用户用过预留的接口可以使用接口的功能而不需要知晓内部具体细节。C++中,通过类实现面向对象的编程,而在基类中只给出纯虚函数的声明,然后在派生类中实现纯虚函数的具体定义的方式实现接口,不同派生类实现接口的方式也不尽相同,从而实现多...

wang13342322203的博客 1万+
上一篇: Leetcode 102:二叉树的层次遍历
下一篇: Leetcode 108:将有序数组转换为二叉搜索树
冀州少主
博客等级 码龄8年 16粉丝 34原创
评论 5
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值