[C/C++]_[初级]_[在Windows上导出C++类的动态库]

场景

  1. Windows上使用VS开发C++类的导出dll时,如果我们的类包含了stl的模板成员变量,那么编译时会报以下警告. 如何解决?
C4251: “Utils::path_”: class“std::basic_string<_Elem,_Traits,_Ax>”需要有 dll 接口由 class“Utils”的客户端使用
1>          with
1>          [
1>              _Elem=char,
1>              _Traits=std::char_traits<char>,
1>              _Ax=std::allocator<char>
1>          ]

说明

  1. 导出C++的类到dll时,我们可以在类的class __declspec(dllexport) Utils{}来声明,之后该类下的所有成员变量和函数都会被导出. 问题来了,stl模板成员变量(非指针)在导出时会导出在该dll的编译设置里的C++对象模型A,而访问或创建这个类Utils时,会在调用的模块(非dll模块)依据这个模块的编译设置创建另一个对象模型B. AB模型不一致时就会导致两个模块访问不同的模型,从而导致程序崩溃。最简单的模型不一致设置就是Debug模式和Release编译出来的模块。还有其他最常见的就是对MSVCR*.dll的依赖方式,比如项目属性-》配置属性-》常规-》MFC的使用里选静态库中使用MFC共享Dll中使用MFC,两种配置生成的dll里的C++对象模型是不同的,往往在Windows里如果不同模块选择的MFC的使用不同而导致运行崩溃。所以为了避免不一致,往往编译不同模块都会选择共享Dll中使用MFC,这样模块可以共享相同的MSVCR*.dll动态库。
    在这里插入图片描述

  2. 还有重要的一点,就是如果有stl的模板类作为成员变量,如果想避免出现C4251警告,那么就把这个成员变量设置为指针变量。当然这个警告可以不理它也可以的,只要保持它的二进制模型一致就行了。比较有效的办法就是在MFC的使用里选择共享Dll中使用MFC

  3. 另一个在项目属性-》C/C++ ->代码生成-》运行库也可以单独设置,这里会根据上边说的MFC的使用设置而联动。所以一般这里不需要动,默认就是选多线程调试 DLL (/MDd).
    在这里插入图片描述

  4. 为什么我强调是Windows?是因为WindowsC++二进制模型不兼容。而LinuxmacOS没这个问题。

  5. 以下是glog库和Gdiplus库的头文件声明里的也是用的指针变量作为成员变量。

glog/logging.h

  1. 使用std::ostringstream *作为CheckOpMessageBuilder的成员变量。
class GOOGLE_GLOG_DLL_DECL CheckOpMessageBuilder {
 public:
  // Inserts "exprtext" and " (" to the stream.
  explicit CheckOpMessageBuilder(const char *exprtext);
  // Deletes "stream_".
  ~CheckOpMessageBuilder();
  // For inserting the first variable.
  std::ostream* ForVar1() { return stream_; }
  // For inserting the second variable (adds an intermediate " vs. ").
  std::ostream* ForVar2();
  // Get the result (inserts the closing ")").
  std::string* NewString();

 private:
  std::ostringstream *stream_;
};

GdiPlusHeaders.h

  1. Gdiplus::Image类的成员nativeImage变量也是声明为指针
class Image : public GdiplusBase
{
...
    GpImage* nativeImage;
    mutable Status lastResult;
    mutable Status loadStatus;

private:
    Image(IN const Image& C);
    Image& operator=(IN const Image& C);
};

例子

  1. 这里写了一个例子来说明导出dllC++类的我认为是最佳而且最方便的做法,就是使用成员指针变量。

test-template-class-export.cpp

// test-template-class-export.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <iostream>
#include "Utils.h"

using namespace std;

static void TestUtil()
{
	Utils u;
	auto date = u.GetNowDate();
	date.append("-1");
	cout << date << endl;

	auto t = u.GetNowTime();
	if(t.first)
		cout << t.second << endl;

	char buf[32] = {0};
	auto dt = u.GetNowDateWithFormat(buf,sizeof(buf),"%Y-%m-%d %H:%M:%S");
	cout << *dt << endl;
	delete dt;
	
	auto length = u.path_.size();
	cout << length << endl;
}

int _tmain(int argc, _TCHAR* argv[])
{
	cout << "Hello world" << endl;
	TestUtil();
	getchar();
	return 0;
}

exp.h

  1. 生成dll时需要加宏LIBWINDOW_EXPORTS.
#ifndef __EXP_H
#define __EXP_H

#ifdef LIBWINDOW_EXPORTS
#define LIB_WINDOW __declspec(dllexport)
#elif LIBWINDOW_EXPORTS
#define LIB_WINDOW
#else
#define LIB_WINDOW __declspec(dllimport)
#endif

#endif

Utils.h


#ifndef UTILS_H
#define UTILS_H

#include <string>
#include "exp.h"
#include <utility>
#include <vector>
#include <map>

class LIB_WINDOW Utils
{
public:
	Utils();
	~Utils();
	std::string GetNowDate();
	std::string* GetNowDateWithFormat(char* buf,size_t bufSize,
		const char* format);
	std::pair<bool,std::string> GetNowTime();

	std::map<int,std::string>* GetDates();
	// 1.这里编译会报 C4251 警告.
	std::string path_;
private:
	// 1.使用指针避免 C4251 警告.
	std::map<int,std::string>* dates_;
};

#endif

Utils.cpp

#include "stdafx.h"
#include "Utils.h"
#include <time.h>

using namespace std;

Utils::Utils()
{
	dates_ = new map<int,string>();
}

Utils::~Utils()
{
	delete dates_;
}

string Utils::GetNowDate()
{
	time_t tt = time(NULL);
	auto m_tm = localtime( &tt );
	char buf[32] = {0};
	strftime(buf,sizeof(buf),"%Y-%m-%d",m_tm);
	return buf;
}

string* Utils::GetNowDateWithFormat(char* buf,size_t bufSize,
		const char* format)
{
	time_t tt = time(NULL);
	auto m_tm = localtime( &tt );
	strftime(buf,bufSize,format,m_tm);
	return new string(buf);
}

pair<bool,string> Utils::GetNowTime()
{
	time_t tt = time(NULL);
	auto m_tm = localtime( &tt );
	char buf[32] = {0};
	strftime(buf,sizeof(buf),"%H:%M:%S",m_tm);
	return make_pair(true,buf);
}

map<int,string>* Utils::GetDates()
{
	return dates_;
}

下载

https://download.csdn.net/download/infoworld/14912526

参考

C/C++ warning C4251: class … 需要有 dll 接口由 class

compiler-warning-level-1-c4251

warning-c4251-when-building-a-dll-that-exports-a-class-containing-an-atlcstrin

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 创作都市 设计师:CSDN官方博客 返回首页
实付 9.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值