DLL函数重载,以及一些前提知识的整理~
来源:互联网 发布:淘宝收藏认准淘行者 编辑:程序博客网 时间:2024/04/30 12:34
__stdcall, __cdecl, extern "C"等一些知识整理下~网上也有不少前辈的精彩文章整理一下放在最下面~
dll会随附生成一个lib,里面存的是函数名什么的,那么可以从他身上下手所以就不必委曲求全的去extern "C"了。也就是用静态调用~本来想用什么宏定义来控制输入输出的~太笨了我~我kao~看别人博客上的转载,如果那样的话,你还只包含一次,那岂不是输出代码就用不了了吗?转不过来弯了~一会试试吧,先把自己的代码弄上来吧,自己备忘~前辈们别笑话~
//dllTest.h
#ifndef LIB_H
#define LIB_H
int __declspec(dllexport)add(int x, int y);
float __declspec(dllexport)add(float x, float y);
#endif
//dllTest.cpp
#include "dllTest.h"
int add(int x, int y)
{
return x + y;
}
float add(float x, float y)
{
return x + y;
}
//main.cpp
#include <stdio.h>
#include <windows.h>
#include <iostream>
#pragma comment(lib,"../Debug/dllTest.lib")
//其实这边若是用宏定义原来头文件时的两个导出和导入就帅多了~包含都文件就可以了~
//__declspec(dllimport)可不要~除非要用static,也是从网上前辈写的文章里看到的~
int __declspec(dllimport) add(int x,int y);
float __declspec(dllimport) add(float x,float y);
int main(int argc, char *argv[])
{
int result = add(2, 3);
float resultf = add(1.1f, 1.1f);
std::cout << result << std::endl;
std::cout << resultf << std::endl;
return 0;
}
//没错代码是从天极网讲动态链接库教程里改动过来的~
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
使用Dependency看DLL的导出函数的名字,会发现有一些有意思的东西,这大多是和编译DLL时候指定DLL导出函数的导出符有关系。
当你使用extern "C"的情况下:
__stdcall会使导出函数名字前面加一个下划线,后面加一个@再加上参数的字节数,比如_Fun@4就是4个字节
__cdecl则是前面仅仅有一个下划线
如果不用extern "C"话则使用C++命名机制,涉及到C++ Name Mangling,比较复杂,编译器之间也不太一样。
另外,__declspec(dllexport)仅会对__cdecl进行处理,去掉前面的下划线(对于一般全局函数来说缺省就是__cdecl),而对于其他两种不会处理。
extern "C"的作用是(防止C++编译器的“名字破坏”特性),使编译器按照C的方式生成函数名,C的方式实际的函数名和你写的一样。如果没有这个,则按照C++的方式生成函数名,这样实际的函数名(LoadLibrary方式GetProcAddress传入的函数名)和你写得函数名不一样,这样你用LoadLibrary、GetProcAddress这种方式调用dll就不成功。
但是用引入库(*.LIB)的方式调用,则编译器自动转换函数名,所以总是没有问题。
深入理解extern "C"
要明白为何需要使用extern "C",还得从C++中对函数的重载处理开始说起。作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在符号库中的名字与C语言的不同。例如,假设某个函数的原型为:
void foo( int x, int y );
该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字(不同的编译器可能生成的名字不同,但是都采用了类似的机制,生成的新名字称为“mangled name”)。_foo_int_int这样的名字包含了函数名、函数参数数量及类型信息,C++就是靠这种机制来实现函数重载的。例如,在C++中,函数void foo( int x, int y )与void foo( int x, float y )编译生成的符号是不相同的,后者为_foo_int_float。
3.“__stdcall调用约定在输出函数名前加上一个下划线前缀,后面加上一个"@"符号和其参数的字节数”
和
“该函数被C编译器编译后在符号库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字”
这两个概念感觉很相似~这两个会不会冲突啊?放进符号表里的是哪个?那另一个是干什么用的?
答:一个是调用约定产生的字符,一个是本身名称进行的修饰
两者合成的名称才是最后的符号名称字符串
//////////////////////////////////////////////////////////////////////////
//天极网的教程真的太牛×了~
4.7 DLL导出类
DLL中定义的类可以在应用工程中使用。
下面的例子里,我们在DLL中定义了point和circle两个类,并在应用工程中引用了它们(单击此处下载本工程)。
//文件名:point.h,point类的声明
#ifndef POINT_H
#define POINT_H
#ifdef DLL_FILE
class _declspec(dllexport) point //导出类point
#else
class _declspec(dllimport) point //导入类point
#endif
{
public:
float y;
float x;
point();
point(float x_coordinate, float y_coordinate);
};
#endif
//文件名:point.cpp,point类的实现
#ifndef DLL_FILE
#define DLL_FILE
#endif
#include "point.h"
//类point的缺省构造函数
point::point()
{
x = 0.0;
y = 0.0;
}
//类point的构造函数
point::point(float x_coordinate, float y_coordinate)
{
x = x_coordinate;
y = y_coordinate;
}
//文件名:circle.h,circle类的声明
#ifndef CIRCLE_H
#define CIRCLE_H
#include "point.h"
#ifdef DLL_FILE
class _declspec(dllexport)circle //导出类circle
#else
class _declspec(dllimport)circle //导入类circle
#endif
{
public:
void SetCentre(const point ¢rePoint);
void SetRadius(float r);
float GetGirth();
float GetArea();
circle();
private:
float radius;
point centre;
};
#endif
//文件名:circle.cpp,circle类的实现
#ifndef DLL_FILE
#define DLL_FILE
#endif
#include "circle.h"
#define PI 3.1415926
//circle类的构造函数
circle::circle()
{
centre = point(0, 0);
radius = 0;
}
//得到圆的面积
float circle::GetArea()
{
return PI *radius * radius;
}
//得到圆的周长
float circle::GetGirth()
{
return 2 *PI * radius;
}
//设置圆心坐标
void circle::SetCentre(const point ¢rePoint)
{
centre = centrePoint;
}
//设置圆的半径
void circle::SetRadius(float r)
{
radius = r;
}
类的引用:
#include "../circle.h" //包含类声明头文件
#pragma comment(lib,"dllTest.lib");
int main(int argc, char *argv[])
{
circle c;
point p(2.0, 2.0);
c.SetCentre(p);
c.SetRadius(1.0);
printf("area:%f girth:%f", c.GetArea(), c.GetGirth());
return 0;
}
从上述源代码可以看出,由于在DLL的类实现代码中定义了宏DLL_FILE,故在DLL的实现中所包含的类声明实际上为:
class _declspec(dllexport) point //导出类point
{
…
}
和
class _declspec(dllexport) circle //导出类circle
{
…
}
而在应用工程中没有定义DLL_FILE,故其包含point.h和circle.h后引入的类声明为:
class _declspec(dllimport) point //导入类point
{
…
}
和
class _declspec(dllimport) circle //导入类circle
{
…
}
不错,正是通过DLL中的
class _declspec(dllexport) class_name //导出类circle
{
…
}
与应用程序中的
class _declspec(dllimport) class_name //导入类
{
…
}
匹对来完成类的导出和导入的!
我们往往通过在类的声明头文件中用一个宏来决定使其编译为class _declspec(dllexport) class_name还是class _declspec(dllimport) class_name版本,这样就不再需要两个头文件。本程序中使用的是:
#ifdef DLL_FILE
class _declspec(dllexport) class_name //导出类
#else
class _declspec(dllimport) class_name //导入类
#endif
实际上,在MFC DLL的讲解中,您将看到比这更简便的方法,而此处仅仅是为了说明_declspec(dllexport)与_declspec(dllimport)匹对的问题。
//
- DLL函数重载,以及一些前提知识的整理~
- dll的一些知识
- 函数的重载整理
- 有关dll的一些知识
- DLL的一些背景知识
- MFC DLL的一些知识
- 关于dll的一些知识
- (2.2.9.1)Groovy一些前提知识
- 网速的一些知识整理
- 函数的一些知识
- 函数的一些知识
- 【备忘】c++重载的一些知识
- [知识整理]Linux系统WIFI知识的一些整理
- [知识整理]Linux系统WIFI知识的一些整理
- 二 函数以及函数的重载
- 2012.12.10整理一些编码的知识
- 关于maven的一些知识整理
- jquery中常用的一些知识整理
- 用cygwin编译qemu源代码
- C++ STL的几种常用“比较”概念简述
- 绘图控件
- 存储过程返回值
- 数据备份(异步拷贝数据)
- DLL函数重载,以及一些前提知识的整理~
- 海明码详解
- C++中的【菱形虚继承】深入剖析
- 读《C和指针》笔记摘要【6】
- 使用MaxScript 读取脚本
- Web Parts(定制)
- 4月9号——毕设大概有头绪了
- multiple plotter,NDIS VISTA WIN7 安全机制
- pthread之线程堆栈