opencv的CV_EXPORT

本文深入解析DLL与Lib的原理及应用,对比两者的使用方式、优缺点,详细讲解__declspec(dllexport)与.def文件在DLL导出函数中的作用,以及DLL与Lib在内存使用和升级便利性上的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CV_EXPORTS 出现的用法:

class CV_EXPORTS MatExpr;

template<typename _Tp> class CV_EXPORTS Size_;

CV_EXPORT的定义:

#if (defined WIN32 || defined WIN64) && defined CVAPI_EXPORTS
    #define CV_EXPORTS __declspec(dllexport)
#else
    #define CV_EXPORTS
#endif

对于DLL的操作重要的函数就是下面这两个:
__declspec(dllimport) ,__declspec(dllexport) ,

__declspec(dllimport)关键字导入函数或者变量。

函数的导入
    当你需要使用DLL中的函数时,往往不需要显示地导入函数,编译器可自动完成。但如果你显示地导入函数,编译器会产生质量更好的代码。由于编译器确切地知道了一个函数是否在一个DLL中,它就可以产生更好的代码,不再需要间接的调用转接。
    Win32的PE格式(Portable Executable Format)把所有导入地址放在一个导入地址表中。下面用一个具体实例说明使用__declspec(dllimport)导入函数和不使用的区别:
    假设func是一个DLL中的函数,现在在要生成的.exe的main函数中调用func函数,并且不显示地导入func函数(即没有:__declspec(dllimport)),代码示例如下:
    int main()
    {
        func();
    }
编译器将产生类似这样的调用代码:
    call func
然后,链接器把该调用翻译为类似这样的代码:
    call 0x40000001       ; ox40000001是"func"的地址
并且,链接器将产生一个Thunk,形如:
    0x40000001: jmp DWORD PTR __imp_func
这里的imp_func是func函数在.exe的导入地址表中的函数槽的地址。然后,加载器只需要在加载时更新.exe的导入地址表即可。
    而如果使用了__declspec(dllimport)显示地导入函数,那么链接器就不会产生Thunk(如果不被要求的话),而直接产生一个间接调用。因此,下面的代码:
    __declspec(dllimport) void func1(void);
    void main(void) 
    {
        func1();
    }
将调用如下调用指令:
    call DWORD PTR __imp_func1
    因此,显示地导入函数能有效减少目标代码(因为不产生Thunk)。另外,在DLL中使用DLL外的函数也可以这样做,从而提高空间和时间效率。
变量的导入
    与函数不同的是,在使用DLL中的变量时,需要显示地导入变量。使用__declspec(dllimport)关键字导入变量。若在DLL中使用.def导出变量,则应使用DATA修饰变量,而不是使用已经被遗弃的CONSTANT。因为CONSTANT可能需要使用指针间接访问变量,不确定什么时候会出问题。
先看代码:以下是在dev-c++里建立自已的dll时的dll.h里面的代码,这里面有一个:_declspec(dllexport)

#ifndef _DLL_H_
#define _DLL_H_//防重复定义

#if BUILDING_DLL
# define DLLIMPORT __declspec (dllexport)
#else /* Not BUILDING_DLL */
# define DLLIMPORT __declspec (dllimport)
#endif /* Not BUILDING_DLL */
DLLIMPORT void HelloWorld (void);
#endif /* _DLL_H_ */

上面代码里面的_delcspce(dllexport)被定义为宏,这样可以提高程序的可读性!这个的作是是将函数定义为导出函数,也就是说这个函数要被包含这个函数的程序之外的程序调用!本语句中就是:void Helloword(void):

摘自msdn:在 32 位编译器版本中,可以使用 __declspec(dllexport) 关键字从 DLL 导出数据、函数、类或类成员函数。__declspec(dllexport) 将导出指令添加到对象文件

若要导出函数,__declspec(dllexport) 关键字必须出现在调用约定关键字的左边(如果指定了关键字)。例如:

__declspec(dllexport) void __cdecl Function1(void);
若要导出类中的所有公共数据成员和成员函数,关键字必须出现在类名的左边,如下所示:

class __declspec(dllexport) CExampleExport : public CObject
{ ... class definition ... };
生成 DLL 时,通常创建一个包含正在导出的函数原型和/或类的头文件,并将 __declspec(dllexport) 添加到头文件中的声明。若要提高代码的可读性,请为 __declspec(dllexport) 定义一个宏并对正在导出的每个符号使用该宏:

#define DllExport   __declspec( dllexport ) 
__declspec(dllexport) 将函数名存储在 DLL 的导出表中。

若要确定用于导出函数的方法(.def 文件或 __declspec(dllexport) 关键字),请回答下列问题:

是否要一直添加附加的导出函数?

谁要使用 DLL?例如,是由许多无法重新生成的可执行文件使用的第三方 DLL 还是仅由可以轻松重新生成的应用程序使用的 DLL?

使用 .DEF 文件的优缺点
在 .def 文件中导出函数使您得以控制导出序号。当将附加的导出函数添加到 DLL 时,可以给它们分配更高的序号值(高于任何其他导出函数)。当您进行此操作时,使用隐式链接的应用程序不必与包含新函数的新导入库重新链接。这非常重要,例如,在设计将由许多应用程序使用的第三方 DLL 时。可以通过添加附加功能不断地增强 DLL,同时确保现有应用程序继续正常使用新的 DLL。MFC DLL 是使用 .def 文件生成的。

使用 .def 文件的另一个优点是:可以使用 NONAME 属性导出函数,该属性仅将序号放到 DLL 的导出表中。对具有大量导出函数的 DLL,使用 NONAME 属性可以减小 DLL 文件的大小。有关编写模块定义语句的信息,请参见模块定义语句的规则。有关序号导出的更多信息,请参见按序号而不是按名称从 DLL 导出函数。

使用 .def 文件的主要缺点是:在 C++ 文件中导出函数时,必须将修饰名放到 .def 文件中,或者通过使用外部“C”用标准 C 链接定义导出函数,以避免编译器进行名称修饰。

如果需要将修饰名放到 .def 文件中,则可以通过使用 DUMPBIN 工具或 /MAP 链接器选项来获取修饰名。请注意,编译器产生的修饰名是编译器特定的。如果将 Visual C++ 编译器产生的修饰名放到 .def 文件中,则链接到 DLL 的应用程序必须也是用相同版本的 Visual C++ 生成的,这样调用应用程序中的修饰名才能与 DLL 的 .def 文件中的导出名相匹配。

使用 __declspec(dllexport) 的优缺点
使用 __declspec(dllexport) 非常方便,因为不必考虑维护 .def 文件和获取导出函数的修饰名。例如,如果您设计的 DLL 供自己控制的应用程序使用,则此方法很适用。如果通过新的导出函数重新生成 DLL,还必须重新生成应用程序,因为如果使用不同版本的编译器进行重新编译,则导出的 C++ 函数的修饰名可能会发生变化。

=========================================================================================

lib与dll的区别:

1. 使用方式有什么不同?

2. 原因是什么?

其实lib无论怎么样,都会用到。

一种是lib单独使用,另外一种是lib+dll的方式(平时所说的dll的方式)。

lib单独使用:

1. LIB包含函数代码本身,不仅包含头文件,还有源码,在编译时直接将代码加入程序当中,称为静态链接库static link library。链接生成的程序可以独立运行。

    即lib文件是静态编译出来的,索引和实现都在其中。

2. 同一机器,多个同种程序运行,每个程序都会加载一份代码库。

3. 任何改动,都需要重新编译、链接,这个对大型的程序非常不便,升级也麻烦。有时,在更改数据结构时,需要重新编译整个工程,而非只编译、链接单个lib。

lib+dll方式:

1. LIB包含了函数所在的DLL文件和文件中函数位置的信息(入口),代码由运行时加载在进程空间中的DLL提供,称为动态链接库dynamic link library。生成的程序需要dll配合使用。

    即lib一般是一些索引信息,记录了dll中函数的入口和位置,dll中是函数的具体内容。应用程序使用LIB文件链接到DLL文件。在应用程序的可执行文件中,存放的不是被调用的函数代码,而是DLL中相应函数代码的地址,从而节省了内存资源。

2. 多个程序运行,共享一份代码。节省内存。

3. 当 DLL 中的函数发生更改时,只要函数的参数和返回值没有更改,就不需重新编译或重新链接使用它们的应用程序。


使用方式的差别:

1. 静态lib
需要.h和lib。
在生成lib时,相当于cpp编译的obj的集合。链接时,指定lib的位置。

2. 动态lib
实质上是dll的使用方式。
在生成dll时,会生成一个lib,lib里包含的是索引信息。
编译链接时,需要.h,lib。
运行时,需要程序和dll。
--------------------- 
作者:langb2014 
来源:优快云 
原文:https://blog.youkuaiyun.com/langb2014/article/details/51286415 
版权声明:本文为博主原创文章,转载请附上博文链接!

<think>我们注意到这段代码是OpenCV库中用于定义图像I/O操作限制的常量。这些常量通过环境变量或配置参数进行初始化,并设置了默认值。这些设置主要用于防止处理过大的图像导致内存溢出或其他问题。 代码解释: 1. `CV_IO_MAX_IMAGE_PARAMS`:定义图像参数的最大数量,默认值为50。 2. `CV_IO_MAX_IMAGE_WIDTH`:定义图像的最大宽度,默认值为2^20(即1048576)。 3. `CV_IO_MAX_IMAGE_HEIGHT`:定义图像的最大高度,默认值为2^20(即1048576)。 4. `CV_IO_MAX_IMAGE_PIXELS`:定义图像的最大像素数量(宽度*高度),默认值为2^30(即1073741824)。 这些常量使用`static const size_t`声明,表示它们是编译时常量,且在文件作用域内可见(如果放在头文件中,则每个包含该头文件的源文件都会有自己的一份拷贝,但因为是常量,所以不会导致链接错误)。不过,通常这些定义会放在一个源文件中,以避免多个定义的问题(如果放在头文件中,应该使用inline或者extern并在源文件中定义)。 函数`utils::getConfigurationParameterSizeT`用于从环境变量或配置文件中获取对应的参数值。如果找不到对应的参数,则使用提供的默认值。 这段代码的作用是设置OpenCV在读取图像时的一些限制,以防止恶意或错误的大图像导致程序崩溃或资源耗尽。 相关问题: 1. 在C++中,`static const`成员变量和普通全局静态常量有什么区别? 2. 为什么在定义这些常量时使用`size_t`类型? 3. OpenCV中如何通过环境变量设置这些参数? 4. 如果图像超过这些限制,OpenCV会如何处理? 注意:根据要求,我们最后需要以指定格式提出四个相关问题。</think>这段代码定义了OpenCV中用于限制图像I/O操作的常量值,通过环境变量可配置这些阈值,防止处理异常大图像导致内存溢出等问题。以下是逐行解释: ```cpp // 从环境变量"OPENCV_IO_MAX_IMAGE_PARAMS"读取值,若无则默认为50 static const size_t CV_IO_MAX_IMAGE_PARAMS = cv::utils::getConfigurationParameterSizeT("OPENCV_IO_MAX_IMAGE_PARAMS", 50); // 从环境变量"OPENCV_IO_MAX_IMAGE_WIDTH"读取值,若无则默认1048576 (2^20) static const size_t CV_IO_MAX_IMAGE_WIDTH = utils::getConfigurationParameterSizeT("OPENCV_IO_MAX_IMAGE_WIDTH", 1 << 20); // 从环境变量"OPENCV_IO_MAX_IMAGE_HEIGHT"读取值,若无则默认1048576 (2^20) static const size_t CV_IO_MAX_IMAGE_HEIGHT = utils::getConfigurationParameterSizeT("OPENCV_IO_MAX_IMAGE_HEIGHT", 1 << 20); // 从环境变量"OPENCV_IO_MAX_IMAGE_PIXELS"读取值,若无则默认1073741824 (2^30) static const size_t CV_IO_MAX_IMAGE_PIXELS = utils::getConfigurationParameterSizeT("OPENCV_IO_MAX_IMAGE_PIXELS", 1 << 30); ``` ### 关键点解析: 1. **作用** - 防止处理超大图像导致内存耗尽(例如恶意构造的畸形图像) - 限制图像解码时的资源消耗(`CV_IO_MAX_IMAGE_PIXELS`限制总像素数) - 控制元数据量(`CV_IO_MAX_IMAGE_PARAMS`限制EXIF等参数的键值对数量) 2. **数据类型** 使用`size_t`保证无符号整数特性(适合表示内存大小),避免负值。 3. **配置机制** `getConfigurationParameterSizeT()`实现逻辑: ```cpp size_t cv::utils::getConfigurationParameterSizeT(const char* name, size_t fallback) { const char* env = getenv(name); // 读取环境变量 if (env && *env != '\0') { return std::strtoull(env, nullptr, 10); // 字符串转无符号长整型 } return fallback; } ``` 4. **位运算技巧** `1 << 20` 等价于 $2^{20}$(位左移运算),比直接写数字更具可读性。 ### 应用场景示例: 当尝试读取10万x10万像素的图像时: ```cpp cv::Mat img = cv::imread("huge_image.tiff"); // 内部检查逻辑: if (img.cols * img.rows > CV_IO_MAX_IMAGE_PIXELS) { throw std::runtime_error("Exceeded max pixel limit!"); } ``` ### 修改配置的方法: 在运行程序前设置环境变量(Linux/macOS): ```bash export OPENCV_IO_MAX_IMAGE_PIXELS=2000000000 ./your_program ``` Windows(命令行): ```cmd set OPENCV_IO_MAX_IMAGE_PIXELS=2000000000 your_program.exe ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值