动态链接库,静态链接库以及关于类的输入输出操作符重载

        先说一下链接库工程生成以后可用的文件,动态链接库工程生成一个.dll文件和一个.lib文件。而静态链接库工程只有一个.lib文件。当然,略提一下,要使用动态链接库或者静态链接库必然要提供库的头文件。有些人把动态库工程生成的.lib文件叫静态链接库,这是错误的,它不是一个静态链接库,只是动态链接库也就是.dll文件的导入库。虽然静态链接库和动态库的导入库都是.lib文件,但是的区别很大,他们实质是不一样的东西。静态库本身就包含了实际执行代码、地址符号表等等,而对于导入库而言,其实际的执行代码位于动态库中,导入库只包含了地址符号表等,确保程序找到对应函数的一些基本地址信息。 实际上导入库.lib文件里面的导出导入函数都是对应到dll文件中相应位置的跳转指令,以执行外部程序时调用dll函数时进行dll跳转。一般情况下,导入库.lib文件里有与相应的dll文件相同的名字,和一个指明dll输出函数入口的顺序表。

 

       那么动态链接库和静态链接库呢?静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib 中的指令都全部被直接包含在最终生成的exe文件中了。但是若使用dll(即动态链接库),该dll 不必被包含在最终exe文件中,exe 文件执行时可以“动态”地引用和卸载这个与exe 独立的dll 文件。静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。动态链接库与静态链接库的使用不同之处在于它允许可执行模块(.dll 文件或 .exe 文件)仅包含在运行时定位dll 函数的可执行代码所需的信息。在静态链接库的使用中,链接器从静态链接库获取所有被引用的函数,并将库同代码一起放到可执行文件中。

 

        首先要明白什么是静态链接库,静态链接库是一个或者多个obj文件的打包,所以有人干脆把从obj文件生成lib的过程称为Archive,即合并到一起。比如你链接一个静态库,如果其中有错,它会准确的找到是哪个obj有错,即静态lib只是壳子。当我们的应用工程在使用静态链接库的时候,静态链接库要参与编译,在生成执行文件之前的链接过程中,将静态链接库的全部指令直接链接进入可执行文件中,故而,在可执行文件生成以后,静态链接库.lib文件即可以弃之不用。

   

        而动态链接库呢,动态链接库 (dll) 是作为共享函数库的可执行文件。动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 .dll文件 中,该 dll 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。dll还有助于共享数据和资源。多个应用程序可同时访问内存中单个dll副本的内容。 使用动态链接代替静态链接有若干优点。DLL 节省内存,减少交换操作,节省磁盘空间,更易于升级,提供售后支持,提供扩展 MFC 库类的机制,支持多语言程序,并使国际版本的创建轻松完成。

 

        明白了动态链接库和静态链接库以及他们的区别,我们来看看他们的创建以及使用。首先创建一个静态链接库工程staticlib,在其中添加staticlib.h和staticlib.cpp,在staticlib.h中声明如下函数:extern "C" int add(int x,int y); //声明为C编译、连接方式的外部函数,在staticlib.cpp中做如下实现:int add(int x,int y){return x + y;}编译工程我们可以得到一个staticlib.lib文件。对于静态链接库的使用,创建一个TestDllSlib工程,第一步,在工程属性->c/c++->附加包含目录中添加statilib.h所在的路径,第二步,在工程属性->连接器->常规->附加库目录中添加staticlib.lib文件所在的路径,第三步,在工程属性->连接器->常规->输入->附加依赖项中添加静态库的名字也就是staticlib.lib。然后我们就可以在测试工程TestDllSlib中需要使用到add函数的地方,先包含静态库的头文件#include "staticlib.h",然后就可以直接使用add函数了,比如在main函数中可以直接int sum = add(2,3);.当然,我们其实可以换种方式,那就是用在调用函数的源代码文件中包含头文件的下方做如下声明:#pragma comment(lib,"staticlib.lib")这样就可以省略前面的第二步第三步了,当然括号里的lib文件处需要保证路径的正确性.至此,静态链接库的创建生成以及使用介绍完了。这里要提及一下,一般我们最主要的关于lib文件的麻烦就是出现unresolved symble 这类错误,这就是lib文件连接错误或者没有包含.c、.cpp文件到工程里。特别是如果在C++工程里用了C语言写的lib文件,就必需要这样包含:

 

鉴于笔者目前的知识,关于在静态库中导出类,还在进一步整理中

 

        下面来看看动态链接库。创建一个动态链接库(.dll)工程MyTestDll。这里面用一个简单的例子解说。在动态链接库工程中添加一个类,类头文件内容如下HironString.h:
#pragma once
#include <iostream>
#include  <iomanip>

#include "Explort.h"
using namespace std;
class MY_DLL CHironString 

{
public:
 CHironString(void);
 CHironString(char * str); 

friend istream& operator>>(istream&,CHironString&);
 friend ostream& operator<<(ostream&,const CHironString&);
 CHironString& operator=(CHironString &other);
 char * GetData();
 CHironString(CHironString &other);
 int Length();
public:
 virtual ~CHironString(void);
private:
 char* m_data;
};头文件内容HironString.cpp如下:

#include "StdAfx.h"
#include <windows.h>
#include "HironString.h"
CHironString::CHironString()
{
 m_data=NULL;
}
CHironString::CHironString(char * str)
{
 int len=strlen(str);
 m_data=new char[len+1];
 strcpy(m_data,str);
}
CHironString::~CHironString()
{
 delete m_data;
}
int CHironString::Length()
{
 return strlen(m_data);
}
CHironString::CHironString(CHironString &other)
{
 int len=strlen(other.m_data)+1;
 m_data=new char[len];
 strcpy(m_data,other.m_data);
}
CHironString& CHironString::operator=(CHironString &other)
{
 if(this==&other)
  return *this;
 if(m_data!=NULL)
  delete[] m_data;
 int len=strlen(other.m_data)+1;
 m_data=new char[len];
 strcpy(m_data,other.m_data);
 return *this;
}
char * CHironString::GetData()
{
 return m_data;
}

导出文件内容如下:Explort.h

#ifdef MYDLL_EXPORTS
#define MY_DLL __declspec(dllexport)
#else
#define MY_DLL __declspec(dllimport)
#endif

//这是导出类的宏定义,将导出类必须加上该宏,才能被导出。
此处的MYDLL_EXPORTS会出现在 属性-->配置属性-->C/C++页面上的 PreProcessor definition中,这个MACRO表明其要定义一个导出宏。

然后编译,我们可以得到一个动态链接库MyTestDll.dll文件,一个动态链接库的导入库MyTestDll.lib。那么剩下就来看看如何使用动态链接库了。由于动态链接库多了一个导入库.lib文件,对于动态链接库我们就有了两种使用方式。

        第一种就是隐式加载:隐式加载的方法同静态链接库的使用方法完全相同,但是必须确认的一点就是,要保证动态链接库.dll文件和你的测试工程TestDllSlib.exe要放在一起。因为 程序一开始运行就需要载入整个dll,无法载入程序就不能开始运行,看一下测试工程中的使用:

#include "stdafx.h"
#include "HironString.h"
#include "staticlib.h"

 

 

还有一点就是由于载入的是整个dll,需要耗费资源较多,这是隐式加载动态链接库一个弊端。但是,我们也可以看到,我们可以直接使用导出类的成员函数class 第二种方式就是显示加载了,相信大多数使用动态链接库的人都知道如何使用了,三个步骤:1,Handle h = LoadLibrary(dllName)将动态链接库动态载入,2,GetProcAddress(h.functionName)返回函数指针,通过函数指针调用动态库内函数,3,FreeLibrary(h)是释放动态库句柄。因此调用程序若想调用DLL中的某个函数就要以某种形式或方式指明它到底想调用哪一个函数。但是无法调用Class method了。关于动态链接库显示加载的例子尚未写出,后续补上

 

 

最后再来看看输入输出操作符的重载:

    为了使dll例子工程里面的string类可以输出和输入,于是想重载输入和输出操作符。因最左边的操作数是ostream,自然就不能用类的成员函数重载,而只能以类的友元函数进行重载 ,于是有了头文件中的两个友元声明。然后在cpp文件中做如下的实现,

istream& operator>>(istream& io,CHironString & s)
{
 const int limit_string_size = 4096;
 char inbuff[limit_string_size];
 io>>setw(limit_string_size)>>inbuff;
 CHironString tmp(inbuff);
 s = tmp;
 return io;
}
ostream& operator<<(ostream& os,CHironString &s)
{
 os<<s.GetData();
 return os;
}

编译通过,生成了dll文件和lib文件,按照动态链接库隐式加载的方法调用,编译发生错误,告诉我操作符重载错误。于是摆渡一下,似乎找到了一种解释,模板类的声明与实现必须要放在同一位置,也就是说要么都在.h中,要么都在.cpp中。于是我把这两个重载函数的实现放到了HironString.h中,编译生成dll和lib文件,在测试工程中调用,果然成功

int _tmain(int argc, _TCHAR* argv[])
{
 CHironString str1("1234");
 CHironString str2 = str1;
 int len =str1.Length();

 std::cout<<str1<<std::endl;  //打印出来1234
 std::cout<<str2<<std::endl;  //打印出来1234

 int sum = add2(2,3);

 return 0;
},于是就把要点摘录下来:

要点1. 重载输入输出操作符,应该作为类的友元函数。

要点2. 返回值应该是输入/输出流的引用( istream& 或者 ostream& )。

要点3. 第一个参数应该是输入/输出流的引用( istream& 或者 ostream& )。

要点4. 第二个参数应该是该类的一个对象的引用。输出流还要求为常量。引用是为了避免隐式赋值带来的开销。

要点5. 将重载定义在类的头文件中


int _tmain(int argc, _TCHAR* argv[])
{
 CHironString str1("1234");
 CHironString str2 = str1;

 int len =str1.Length();

 int sum = add2(2,3);

 return 0;
}

 

摆渡资料:http://hi.baidu.com/zhleilei/blog/item/1ba98c4502ce2b388794739d.html

http://www.cnblogs.com/winston/archive/2008/07/05/1236273.html

http://www.cnblogs.com/chio/archive/2008/08/05/1261296.html

http://blog.csdn.net/huozi19820418/archive/2008/06/19/2565911.aspx

http://topic.csdn.net/t/20051005/20/4308418.html

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值