Ter*_*ary 3 c++ compiler-construction dll linker visual-c++
我正在努力增加对基本库链接,依赖关系等的理解.我创建了一个包含三个项目的Visual Studio解决方案
静态lib使用/MTd单个类(Foo),一个方法int GetNum() { return 5; }
与单个class()共享dll使用,一个方法/MDdBarint GetNum() { Foo f; return f.GetNum(); }
Win32控制台应用程序.那叫Bar b; std::cout << b.GetNum() << std::endl
当我试图构建它时,它抱怨它无法找到我的dll的关联库.做了一点研究,看到我需要添加__declspec(dllexport)到我的GetNum()方法中,我会得到一个.lib.凉.
接下来是控制台应用程序说它无法找到静态库Foo.我把它添加到我的引用中,它都构建并运行良好.
我的问题是 - 为什么我的exe需要知道什么Foo?我想在所有依赖项中有效地"烘焙"到dll中,所以我可以分享它,链接到它,并且很高兴去.
这不是语言的工作方式或我缺少的设置/模式吗?我的最终目标是能够构建一个封装第三方.lib的使用的DLL,而不是让客户端应用程序需要担心添加对所有这些的引用.
更新
这是大部分代码.
// ---------------------- Lib (e.g. Foo)
#pragma once
class MathLib
{
public:
MathLib(void);
~MathLib(void);
int GetNum() { return 83; }
};
// ---------------------- DLL (e.g. Bar)
#pragma once
#ifdef CONSOLETEST_EXPORT
#define CONSOLETEST_API __declspec(dllexport)
#else
#define CONSOLETEST_API __declspec(dllimport)
#endif
#include "MathLib.h"
class MathDll
{
public:
__declspec(dllexport) MathDll(void);
__declspec(dllexport) ~MathDll(void);
__declspec(dllexport) int GetNumFromDyn()
{
MathLib m;
return m.GetNum();
}
};
// ---------------------- exe
int _tmain(int argc, _TCHAR* argv[])
{
MathDll m;
std::cout << "num is " << m.GetNumFromDyn() << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
用C/C++,它跨越正确建构的代码是非常重要的报头(例如h,hpp,hxx,h++,等)和翻译单元(通常称为来源,例如c,cpp,cxx,c++,等).当你设计一个库时,你应该不断思考什么属于它的界面(即应该被消费者看到)以及什么属于它的实现(即不应该被消费者看到).
记住经验法则 - 消费者可以看到任何标题中存在的所有符号(如果包括在内),因此消费者需要在某个时间点的链接阶段解决!
这基本上就是你玩具示例中发生的事情.因此,让我们通过使用一个简单的规则来解决它,你应该记住它:尽可能多地放入翻译单元,即保持标题最小化.现在让我们用你的例子来说明它是如何工作的:
MathLib.hpp:
#pragma once
class MathLib {
public:
MathLib();
~MathLib();
int GetNum();
};
Run Code Online (Sandbox Code Playgroud)
MathLib.cpp:
#include "MathLib.hpp"
MathLib::MathLib() {}
MathLib::~MathLib() {}
int MathLib::GetNum() { return 83; }
Run Code Online (Sandbox Code Playgroud)
现在构建MathLib.cpp为静态库.
MathDll.hpp:
#pragma once
#ifdef CONSOLETEST_EXPORT
# define CONSOLETEST_API __declspec(dllexport)
#else
# define CONSOLETEST_API __declspec(dllimport)
#endif
class CONSOLETEST_API MathDll {
public:
MathDll();
~MathDll();
int GetNumFromDyn();
};
Run Code Online (Sandbox Code Playgroud)
MathDll.cpp:
#include "MathDll.hpp"
#include "MathLib.hpp"
MathDll::MathDll() {}
MathDll::~MathDll() {}
int MathDll::GetNumFromDyn() {
MathLib m;
return m.GetNum();
}
Run Code Online (Sandbox Code Playgroud)
现在构建MathDll.cpp为动态链接库(DLL)并且不要忘记CONSOLETEST_EXPORT在构建期间添加定义,这样CONSOLETEST_API就可以生成导出符号(即类及其方法)__declspec(dllexport)的导入库.MathDllDLL.在MSVC上,您可以通过添加/DCONSOLETEST_API到编译器的调用来实现此目的.最后,在构建这个DLL时,肯定会将它与之前构建的静态库链接起来MathLib.lib.
注意:最好像我上面那样导出整个类class CONSOLETEST_API MathDll,而不是单独导出所有方法.
main.cpp:
#include "MathDll.hpp"
#include <iostream>
int _tmain(int argc, _TCHAR* argv[]) {
MathDll m;
std::cout << "num is " << m.GetNumFromDyn() << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
现在构建main.cpp为控制台应用程序,并仅将其与先前构建的DLL导入库链接MathDll.lib.
请注意问题是如何消失的,因为我已经摆脱了传递依赖MathLib(MathDll.hppfrom)from main.cpp,因为现在#include "MathLib.hpp"包含是在翻译单元中完成的MathDll.cpp(因为它实际上只是根据上述规则需要),因此被构建为二进制工件(在这种情况下为DLL)并且不存在于其接口中.
了解所有这些对于使用C/C++进行适当的本机软件开发非常重要,所以事先提出这个问题真是太好了.我遇到了那些经常不了解/不了解这一点的人,这对他们(业余爱好者)和我们来说是彻头彻尾的噩梦,当我们不得不处理他们写的那些糟糕的软件时......