DLL函数重载,以及一些前提知识的整理~

__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 &centrePoint);
  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 &centrePoint)
{
 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)匹对的问题。
//

 

 

 

 

 

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值