C++中lib和dll解析

(一)概念

首先介绍一下静态库(静态链接库)、动态库(动态链接库)的概念,首先两者都是代码共享的方式。
静态库:在链接步骤中,连接器将从库文件取得所需的代码,复制到生成的可执行文件中,这种库称为静态库,其特点是可执行文件中包含了库代码的一份完整拷贝;缺点就是被多次使用就会有多份冗余拷贝。即静态库中的指令都全部被直接包含在最终生成的 EXE 文件中了。在vs中新建生成静态库的工程,编译生成成功后,只产生一个.lib文件。
动态库:动态链接库是一个包含可由多个程序同时使用的代码和数据的库,DLL不是可执行文件。动态链接提供了一种方法,使进程可以调用不属于其可执行代码的函数。函数的可执行代码位于一个 DLL 中,该 DLL 包含一个或多个已被编译、链接并与使用它们的进程分开存储的函数。在vs中新建生成动态库的工程,编译成功后,产生一个.lib文件和一个.dll文件。
那么上述静态库和动态库中的lib有什么区别呢?
静态库中的lib:该LIB包含函数代码本身(即包括函数的索引,也包括实现),在编译时直接将代码加入程序当中
动态库中的lib:该LIB包含了函数所在的DLL文件和文件中函数位置的信息(索引),函数实现代码由运行时加载在进程空间中的DLL提供
总之,lib是编译时用到的,dll是运行时用到的。如果要完成源代码的编译,只需要lib;如果要使动态链接的程序运行起来,只需要dll。

(二)生成和使用lib

1.VS2015生成lib文件
在VS2015中新建项目->Win32项目->勾选静态链接库,然后添加所需要的类,包括.h文件和.cpp文件。下面的例子中,我添加了两个类,分别是twomain类和student类,相应的代码如下:
towmain.h:

#pragma once
#include"stdafx.h"
#include"student.h"
#include<iostream>
#include<string>
using namespace std;

class twomain
{
public:
    twomain();
    ~twomain();

    void setStn(string name, int age);
    student getStn();
    void setCity(string city);
    string getCity();

    void toString();

private:
    student stn;
    string city;
};

towmain.cpp:

#include "stdafx.h"
#include "twomain.h"

twomain::twomain()
{
}
twomain::~twomain()
{
}

void twomain::setCity(string city) {
    this->city = city;
}
string twomain::getCity() {
    return this->city;
}

void twomain::setStn(string name, int age) {
    this->stn.setName(name);
    this->stn.setAge(age);
}
student twomain::getStn() {
    return this->stn;
}

void twomain::toString() {
    cout<<"name is "<<this->stn.getName()<<",age is "<<this->stn.getAge()<<",city is "<<this->city<<endl;
}

student.h:

#pragma once
#include<iostream>
#include<string>
using namespace std;
class student
{
public:
    student();
    ~student();
    void setName(string name);
    string getName();
    void setAge(int age);
    int getAge();

    void toString();

private:
    string name;
    int age;
};

student.cpp:

#include "stdafx.h"
#include "student.h"
student::student()
{
}
student::~student()
{
}

void student::setName(string name) {
    this->name = name;
}
string student::getName() {
    return this->name;
}

void student::setAge(int age) {
    this->age = age;
}
int student::getAge() {
    return this->age;
}

void student::toString() {
    cout << "name is " << this->name << ",age is " << this->age << endl;
}

然后点击工具栏菜单中的生成->生成解决方案,就可以在项目文件目录的debug目录下看到生成的lib文件。主要要考虑清楚自己的lib应用的场景,是debug模式还是release模式,是x86还是x64平台使用,这些要严格按照需要选择,不然后面使用时很容易出一些错误。
2.使用lib文件
生成好了lib文件之后,我们新建一个空项目,来测试生成的lib是否好用。在新建的空项目中添加main.cpp文件,并在该文件中添加如下代码:

#include"twomain.h"
int main() {
    twomain t;
    t.setCity("江西");
    t.setStn("刘伟",23);

    t.toString();
    system("pause");

    return 0;
}

这时还没有引入.h文件和.lib文件,所以会有错误,引入.h文件和.lib文件的操作如下:
1.引入.h文件。引入点.h文件,使得在#include .h文件时不会报错,也就引入了类的声明,类的具体实现在.lib文件中。右键项目->属性->配置属性->C/C++->常规->附加包含目录,在附加包含目录中添加包含该lib中使用到所有的.h的文件夹的路径。项目首先根据会自己项目中搜索是否存在.h文件,如果发现没有就会去附加包含目录中搜索.h文件。如下图所示:
引入.h所在文件夹
2.引入.lib文件。.h文件仅仅包含了类或方法的定义,没有具体的实现,具体的实现在.lib文件中。右键项目->属性->配置属性->链接器->常规->附加库目录,在附加库目录中添加包含该lib文件的文件夹路径。然后再在链接器->输入->附加依赖项中,加入该lib的名字,包括文件格式.lib。如下图所示:
引入lib所在文件夹
引入lib

然后程序就可以运行了,参考main.cpp代码,运行结果截图如下:
运行结果

(三)生成和使用dll

1.VS2015生成dll
在VS2015中新建项目->Win32项目->勾选dll,然后添加所需要的类,包括.h文件和.cpp文件。我在项目中添加了dll和student两个类,代码如下:
dll.h:

#pragma once
//_declspec(dllimport),用来导入
/*
*默认情况下是自动生成导出DLL_EXPORT,如果DLL_EXPORT已经存在,则定义DLL_API为_declspec(dllexport),
*如果DLL_EXPORT不存在,则定义DLL_API为_declspec(dllimport)
*/
#ifdef DLL_EXPORTS
#define DLL_API _declspec(dllexport)
#else
#define DLL_API _declspec(dllimport)
#endif
#include<iostream>
#include<string>
#include"student.h"
using namespace std;

class DLL_API dll {
public:
    dll();
    ~dll();

    void setStn(string name, int age);
    student getStn();
    void setCity(string city);
    string getCity();

    void toString();

private:
    student stn;
    string city;
};

extern "C" DLL_API void cal(int x);

在dll.h文件中,我首先定义了dll接口,DLL_API。当我们引用dll时,只能引用dll中用dll接口声明的类,函数,变量等,所以一定要定义dll接口。然后我们用该接口声明了一个dll类和一个函数cal(int x),函数前面加上extern “C”是防止dll文件中改变这个函数的函数名,因为C++编译dll函数时,会根据需要更改命名,添加extern “C”就会阻止名字的更改,保持原始的命名。以上.h文件的代码也可以这样子,代码如下:
dll.h(版本2):

#pragma once
//_declspec(dllimport),用来导入
/*
*默认情况下是自动生成导出DLL_EXPORT,如果DLL_EXPORT已经存在,则定义DLL_API为_declspec(dllexport),
*如果DLL_EXPORT不存在,则定义DLL_API为_declspec(dllimport)
*/
#ifdef DLL_EXPORTS
#define DLL_API _declspec(dllexport)
#else
#define DLL_API _declspec(dllimport)
#endif
#include<iostream>
#include<string>
#include"student.h"
using namespace std;

class dll {
public:
    DLL_API dll();
    DLL_API ~dll();

    DLL_API void setStn(string name, int age);
    DLL_API student getStn();
    DLL_API void setCity(string city);
    DLL_API string getCity();

    DLL_API void toString();

private:
    student stn;
    string city;
};

extern "C" DLL_API void cal(int x);

dll.cpp:

// dll.cpp : 定义 DLL 应用程序的导出函数。
//

#include "stdafx.h"
#include"dll.h"

dll::dll() {

}
dll::~dll() {

}

void dll::setStn(string name, int age) {
    this->stn.setName(name);
    this->stn.setAge(age);
}
student dll::getStn() {
    return this->stn;
}

void dll::setCity(string city) {
    this->city = city;
}
string dll::getCity() {
    return this->city;
}

void dll::toString() {
    cout << "name is " << this->stn.getName() << ",age is " << this->stn.getAge() << ",city is " << this->getCity() << endl;
}

DLL_API void cal(int x) {
    cout <<x<< "的平方是:" << x*x << endl;
}

student.h:

#pragma once

#include<string>
#include<iostream>
using namespace std;

class student
{
public:
    student();
    ~student();

    void setName(string name);
    string getName();
    void setAge(int age);
    int getAge();

    void toString();

private:
    string name;
    int age;
};

student.cpp:

#include "stdafx.h"
#include "student.h"

student::student()
{
}
student::~student()
{
}

void student::setName(string name) {
    this->name = name;
}
string student::getName() {
    return this->name;
}

void student::setAge(int age) {
    this->age = age;
}
int student::getAge() {
    return this->age;
}

void student::toString() {
    cout << "name is " << this->name << ",age is " << this->age << endl;
}

然后点击工具栏中的生成->生成解决方案,就会在项目文件路径中的debug目录下生成相应的.dll和.lib文件。在上面的概念中已经说明了,dll链接库中,.h文件给出了类或函数的声明,.lib文件给出了这些类和函数的实现在dll文件中的位置,dll文件中实现了相应的类和函数的声明。注意,要考虑后面使用dll的场景,是debug模式还是release模式,是x86还是x64环境,根据需要来调整环境,VS2015调整环境很简单,然后生成相应的dll文件。

2.使用dll文件
使用dll文件包括隐式调用和显式调用。隐式调用需要.h文件,.lib文件和.dll文件,而显式调用只需要.dll文件。
2.1.隐式调用dll
隐式调用dll方法和上面调用lib基本上是一模一样,只要一点点差别。以下面例子为例:
首先新建一个空项目,添加相应的main.cpp,代码如下:

#include"dll.h"

int main() {
    dll d;
    d.setCity("江西");
    d.setStn("刘伟",23);
    d.toString();

    system("pause");
    return 0;
}

然后也是先引入lib中包含的.h文件所在文件夹的路径,然后再引入相应的.lib文件所在文件夹路径和引入.lib文件,具体操作和上面使用lib是一样的。最后需要在这个新建的项目所在的文件中找到debug文件夹,将上面生成的.dll文件添加到这个debug目录中(如果是release模式,则是release文件夹下)。然后就可以执行程序,执行结果截图如下:
执行结果

2.2隐式调用dll文件
隐式调用dll文件只需要将该dll文件放到需要使用该dll文件的项目下的debug文件夹中,不需要引入.h文件和.lib文件,因为隐式调用dll是用win api来找到函数的定义。隐式调用dll文件一般只能调用dll中的方法,不能调用dll中的类和变量(其实也可以调用,只是很麻烦)。下面给出了隐式调用dll文件的例子,仍以上面生成的dll文件为例子,调用该dll文件中的cal(int x)方法,代码如下:

#include<Windows.h>                                   //使用win api需要的头文件
#include<iostream>
using namespace std;

void useCal(int x) {
    typedef int(*dlltest)(int x);                     //定义指向dll中函数的函数指针,保证参数类型一致,(int x)表示有一个int型参数
    HINSTANCE hlib = LoadLibrary("dll.dll");          //加载dll,使用相对路径,从项目文件路径开始
    if (!hlib) {
        cout << "load dll error" << endl;
    }

    dlltest cal = (dlltest)GetProcAddress(hlib,"cal");//得到函数的定义
    if (!cal) {
        cout << "didn't find the method!" << endl;
    }

    cal(x);                                           //执行函数
}
int main() {
    useCal(5);
    system("pause");
    return 0;
}

先用指向函数的指针去dll文件中找到函数的定义,然后执行函数。执行结果截图如下:
执行结果

参考链接:
http://www.cnblogs.com/TenosDoIt/p/3203137.html
http://www.cnblogs.com/enterBeijingThreetimes/archive/2010/08/04/1792099.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值