自己编写了一个试样程序,用来测试编写的类是否正常运行。
思路:将该类在dll中导出,在控制台程序中隐式加载该dll(具体操作就是lib+dll的方法),然后就是正常调用该类的功能。尤其,我在类中自定义了迭代器,应该支持范围for(range for)操作。
Note:内含CLeap类和它的容器类CLeaps类;该类主要实现值班跳岗原因的记录功能。
出现错误如下:
就是这样,提示vector无法解引用!!!
附上代码控制台程序代码:
1 // use-dll.cpp : 定义控制台应用程序的入口点。 2 // 3 4 #include "stdafx.h" 5 #include "DLL_Leaps.h" 6 7 int _tmain(int argc, _TCHAR* argv[]) 8 { 9 CLeaps myleaps; 10 myleaps.setfile("data.dat"); 11 //初始化一个leap 12 CLeap myleap0(string("do activity_0"),time(nullptr),false); 13 CLeap myleap1(string("do activity_1"),time(nullptr),false); 14 CLeap myleap2(string("do activity_2"),time(nullptr),false); 15 myleaps.append(myleap0); 16 myleaps.append(myleap1); 17 myleaps.append(myleap2); 18 cout<<std::setiosflags(ios::left) 19 <<setw(20)<<"reason" 20 <<setw(20)<<"time" 21 <<setw(20)<<"execute_state" 22 <<endl; 23 24 for(auto obj=myleaps.begin();obj!=myleaps.end();obj++) 25 { 26 leapRecord r=obj->enquireRecord(); 27 char t[255]; 28 ctime_s(t,255,&r.time); 29 cout<<std::setiosflags(ios::left) 30 <<setw(20)<<r.reason 31 <<setw(20)<<t 32 <<setw(20)<<r.executed 33 <<endl; 34 } 35 36 bool res=myleaps.save(); 37 if (!res) cout<<"save fail!"<<endl; 38 else cout<<"save sucessed!"<<endl; 39 40 system("pause"); 41 return 0; 42 }
其中DLL_Leaps.h就是dll的类声明:
1 // 下列 ifdef 块是创建使从 DLL 导出更简单的 2 // 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 DLL_LEAPS_EXPORTS 3 // 符号编译的。在使用此 DLL 的 4 // 任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将 5 // DLL_LEAPS_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的 6 // 符号视为是被导出的。 7 #ifdef DLL_LEAPS_EXPORTS 8 #define DLL_LEAPS_API __declspec(dllexport) 9 #else 10 #define DLL_LEAPS_API __declspec(dllimport) 11 #endif 12 13 // 此类是从 DLL_Leaps.dll 导出的 14 #include "Leap.h" 15 #include "Leaps.h"
Leaps.h文件:
1 #ifndef LEAPS_H 2 #define LEAPS_H 3 4 #include "Leap.h" 5 #include <vector> 6 using std::vector; 7 #include <fstream> 8 using std::fstream; 9 using std::ios; 10 //为实现文件删除,改名操作 11 #include <stdio.h> 12 13 struct leapRecord; 14 15 struct DLL_LEAPS_API leapRecords 16 { 17 //标识输出到文件的大小而非对齐后大小 18 int cbsize; 19 leapRecord *records; 20 }; 21 22 class DLL_LEAPS_API CLeaps 23 { 24 public: 25 CLeaps():leaps(0) 26 { 27 now=nullptr; 28 records.cbsize=0; 29 records.records=nullptr; 30 } 31 32 ~CLeaps(void); 33 34 //请求跳岗记录集 35 leapRecords enquireRecords(); 36 //销毁传输使用的记录集 37 void destroyRecords(); 38 39 //自定义迭代器 40 typedef CLeap* iterator; 41 42 void append(CLeap& leap); 43 void remove(const CLeap& leap); 44 void clear(void); 45 46 int size()const; 47 48 iterator begin(); 49 iterator end(); 50 51 CLeap& operator[](int selector); 52 53 iterator operator++(); 54 iterator operator++(int); 55 iterator operator--(); 56 iterator operator--(int); 57 CLeap& operator*(); 58 bool operator==(iterator& ite); 59 bool operator!=(iterator& ite); 60 61 //文件保存位置 62 void setfile(string f); 63 const string& getfile()const; 64 65 bool save(); 66 bool load(); 67 68 private: 69 70 //CLeap组 71 vector<CLeap> leaps; 72 //支持迭代器,标记当前位置 73 iterator now; 74 //用来输出跳岗记录集 75 leapRecords records; 76 77 string file; 78 }; 79 80 #endif
Leap.h头文件如下:
1 #ifndef LEAP_H 2 #define LEAP_H 3 4 #include <string> 5 #include <ctime> 6 using std::string; 7 8 struct DLL_LEAPS_API leapRecord 9 { 10 bool executed; 11 char reason[255]; 12 long long time; 13 }; 14 15 class DLL_LEAPS_API CLeap 16 { 17 public: 18 CLeap(){} 19 CLeap(string rsn,time_t timestamp,bool flag) 20 :reason(rsn),recordtime(timestamp),hasExecuted(flag){} 21 ~CLeap(void); 22 23 //请求跳岗记录 24 leapRecord enquireRecord(); 25 26 //更新跳岗执行状态:true为已跳岗 27 void updateExecuteStat(bool stat){hasExecuted=stat;} 28 //获取跳岗状态 29 bool getExecuteStat()const{return hasExecuted;} 30 31 bool operator==(const CLeap& leap)const; 32 bool operator!=(const CLeap& leap)const{return !(operator==(leap));} 33 34 private: 35 //跳岗原因 36 string reason; 37 //记录生成时间 38 time_t recordtime; 39 //标识是否已经跳岗 40 bool hasExecuted; 41 }; 42 43 #endif
如果需要dll的实现代码,我再附上。
当然,由于导出类中含有vetctor,string这种含模板的STL类,毫无疑问会warning,我直接选择无视了。不知道是否对这个bug有所影响。
C++现在越来越少人关注它,以至于问题抛出之后经常石沉大海,作为一个C++的新手,我还是对大神突然降临满怀期待的!
大神啊,救救我这团团转的小程序猿吧!
Tip:我的感觉是迭代器定义出现问题了。所以导致无法解引用。
上述我定义的
1 //该运算符重载是在leaps.h中实现的
2 CLeap& operator*();
所以对迭代器隐式解引用肯定会失败!
这个问题有点复杂,涉及二进制兼容 stl版本 debug/relase模式等诸多方面
https://stackoverflow.com/questions/10573130/implications-of-using-stdvector-in-a-dll-exported-function 这个资料可以参考
Using std::vector in a DLL interface would require all the clients of that DLL to be compiled with the same version of the same compiler because STL containers are not binary compatible. Even worse, depending on the use of that DLL by clients conjointly with other DLLs, the ''instable'' DLL API can break these client applications when system updates are installed (e.g. Microsoft KB packages) (really?).
Despite the above, if required, std::vector can be used in a DLL API by exporting std::vector<B>
like:
though, this is usually mentioned in the context when one wants to use std::vector as a member of A (http://support.microsoft.com/kb/168958).
The following Microsoft Support Article discusses how to access std::vector objects created in a DLL through a pointer or reference from within the executable (http://support.microsoft.com/default.aspx?scid=kb;EN-US;Q172396). The above solution to use template class EXPORT ...
seems to be applicable too. However, the drawback summarized under the first bullet point seems to remain.
To completely get rid of the problem, one would need to wrap std::vector and change the signature of myFunction
, PIMPL etc..
这篇博文我看过了,我先去实践看下效果。
除此之外,我重新定义了迭代器(参考网络版本)
定义如下:
1 #ifndef LEAPS_H 2 #define LEAPS_H 3 4 #include "Leap.h" 5 #include <vector> 6 using std::vector; 7 #include <fstream> 8 using std::fstream; 9 using std::ios; 10 //为实现文件删除,改名操作 11 #include <stdio.h> 12 13 struct leapRecord; 14 15 struct DLL_LEAPS_API leapRecords 16 { 17 //标识输出到文件的大小而非对齐后大小 18 int cbsize; 19 leapRecord *records; 20 }; 21 22 class DLL_LEAPS_API CLeaps 23 { 24 public: 25 CLeaps():leaps(0) 26 { 27 records.cbsize=0; 28 records.records=nullptr; 29 } 30 31 ~CLeaps(void); 32 33 //请求跳岗记录集 34 leapRecords enquireRecords(); 35 //销毁传输使用的记录集 36 void destroyRecords(); 37 38 void append(CLeap& leap); 39 void remove(const CLeap& leap); 40 void clear(void); 41 42 int size()const; 43 44 CLeap& operator[](int selector); 45 46 class DLL_LEAPS_API const_iterator 47 { 48 private: 49 CLeap* _pNow; 50 public: 51 const_iterator(CLeap *p = nullptr):_pNow(p){} 52 const_iterator& operator=(CLeap *p){ _pNow=p; return *this;} 53 const const_iterator operator++(); 54 const const_iterator operator++(int); 55 56 const CLeap& operator*()const; 57 CLeap& operator*(); 58 59 bool operator==(const const_iterator& iter)const; 60 bool operator!=(const const_iterator& iter)const; 61 62 }; 63 //自定义迭代器 64 typedef const_iterator iterator; 65 66 iterator begin(); 67 iterator end(); 68 69 //文件保存位置 70 void setfile(string f); 71 const string& getfile()const; 72 73 bool save(); 74 bool load(); 75 76 private: 77 78 //CLeap组 79 vector<CLeap> leaps; 80 //用来输出跳岗记录集 81 leapRecords records; 82 83 string file; 84 }; 85 86 #endif
这是新版本的Leaps.h
但是这个问题依然还是存在。。。
我按照上述方法,更新了DLL头文件,具体如下:
1 #ifdef DLL_LEAPS_EXPORTS 2 #define DLL_LEAPS_API __declspec(dllexport) 3 #else 4 #define DLL_LEAPS_API __declspec(dllimport) 5 #endif 6 7 // 此类是从 DLL_Leaps.dll 导出的 8 #include "Leap.h" 9 #include "Leaps.h" 10 11 12 //新 增 13 // 14 extern template class DLL_LEAPS_API std::vector<CLeap>; 15 extern template class DLL_LEAPS_API std::allocator<CLeap>;
然而问题还是没有好转。
这可如何是好,难道要我用上面提到的第四种方案,生成一个代理类???