c++ - DLL 将抽象基类作为参数导出到成员函数 - C++
问题描述
我想知道如何__declspec(dllexport)
使用具有抽象基类(指向的智能指针)作为参数的成员函数导出 DLL(使用 - 所以,Windows,使用 MSVC)。
目前,我收到错误 C2280:std::unique_ptr<Base>...
尝试引用已删除的函数。这是一个例子:
动态链接库:
类.h
#ifdef DLLFILE //defined in MSVC for compiling DLL, not defined for exe
#define DLLEXP __declspec(dllexport)
#else
#define DLLEXP __declspec(dllimport)
#endif /* DLLFILE */
#include <vector>
#include <memory>
#include <functional>
class DLLEXP Base
{ //Base class that provides a pure virtual interface to a function that could have a variety of implementations
public:
virtual double fieldAtS(double s) = 0;
};
class DLLEXP Derived : public Base
{ //Some derived implementation of Base
private:
std::function<double(double)> fcn_m;
public:
Derived(std::function<double(double)> fcn);
double fieldAtS(double s) override;
};
class DLLEXP Container
{ //Container class that stores implementations of Derived and determines the sum of all implementations
private:
std::vector<std::unique_ptr<Base>> elems_m;
public:
void add(std::unique_ptr<Base> ptr);
double fieldAtS(double s);
};
类.cpp
#include "class.h"
#include <iostream>
#include <memory>
Derived::Derived(std::function<double(double)> fcn) : fcn_m{ fcn }
{
}
double Derived::fieldAtS(double s)
{
return fcn_m(s);
}
void Container::add(std::unique_ptr<Base> ptr)
{
elems_m.push_back(std::move(ptr));
}
double Container::fieldAtS(double s)
{
double ret{ 0.0 };
for (auto& der : elems_m)
ret += der->fieldAtS(s);
return ret;
}
EXE文件:
主文件
#include "class.h"
#include <memory>
#include <iostream>
int main()
{
std::unique_ptr<Container> mine{ std::make_unique<Container>() };
mine->add(std::make_unique<Derived>([](double x) { return 10.0 * x; }));
mine->add(std::make_unique<Derived>([](double x) { return x; }));
mine->add(std::make_unique<Derived>([](double x) { return x / 10.0; }));
std::cout << mine->fieldAtS(5.0) << std::endl;
return 0;
}
我想我明白这里发生了什么(检查我的理解)......即,编译器正在编译Container::add(unique_ptr<Base>)
,并且由于指定了 dllexport,因此必须在 DLL 中包含一个字节码版本unique_ptr<Base>
(而不是依赖于它在运行时 - 这是发生了什么吗?),编译器无法做到这一点,因为Base
. 我在正确的轨道上吗?
我的目标是:
- 实现多态性
fieldAtS
- 也就是说,我希望能够在运行时选择一些字段模型(并且在适当的时候,将多个添加到Container
/求和它们以从所有贡献的元素中获取总字段) - 从 DLL 中导出这些类并在其他地方调用它们
- 仍然使用智能指针(在我的代码中的其他地方,我导出了动态创建类实例并传递(哑)指针的函数,以及在其他情况下接收哑指针并调用成员函数它 - 想要避免这种情况)。
所以有几个问题:
- 我对正在发生的事情的理解正确吗?
- 对于必须(也许不必)
dllexport
'ed的类来说,这是一个好的设计方法吗? - 我将如何在这里实现我的目标?也就是说,如果这是好的设计,我该如何正确导出容器?如果没有,我还应该如何构建我的代码?
编辑:构建日志/更新信息
似乎错误是指默认的复制构造函数:Base& Base::operator=(const Base&) = delete;
...所以,如果我要明确声明一个复制构造函数dllexport
?
构建日志如下:
1>------ Rebuild All started: Project: DLL, Configuration: Release x64 ------
1> dllmain.cpp
1>%BASEFOLDER%\include\class.h(18): warning C4251: 'Derived::fcn_m': class 'std::function<double (double)>' needs to have dll-interface to be used by clients of class 'Derived'
1> %BASEFOLDER%\include\class.h(18): note: see declaration of 'std::function<double (double)>'
1>%BASEFOLDER%\include\class.h(28): warning C4251: 'Container::elems_m': class 'std::vector<std::unique_ptr<Base,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>>' needs to have dll-interface to be used by clients of class 'Container'
1> with
1> [
1> _Ty=Base
1> ]
1> %BASEFOLDER%\include\class.h(28): note: see declaration of 'std::vector<std::unique_ptr<Base,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>>'
1> with
1> [
1> _Ty=Base
1> ]
1>C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\xutility(2316): error C2280: 'std::unique_ptr<Base,std::default_delete<_Ty>> &std::unique_ptr<_Ty,std::default_delete<_Ty>>::operator =(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)': attempting to reference a deleted function
1> with
1> [
1> _Ty=Base
1> ]
1> C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\memory(1436): note: see declaration of 'std::unique_ptr<Base,std::default_delete<_Ty>>::operator ='
1> with
1> [
1> _Ty=Base
1> ]
1> C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\xutility(2335): note: see reference to function template instantiation '_OutIt std::_Copy_unchecked1<_InIt,_OutIt>(_InIt,_InIt,_OutIt,std::_General_ptr_iterator_tag)' being compiled
1> with
1> [
1> _OutIt=std::unique_ptr<Base,std::default_delete<Base>> *,
1> _InIt=std::unique_ptr<Base,std::default_delete<Base>> *
1> ]
1> C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\vector(997): note: see reference to function template instantiation '_OutIt *std::_Copy_unchecked<std::unique_ptr<Base,std::default_delete<_Ty>>*,std::unique_ptr<_Ty,std::default_delete<_Ty>>*>(_InIt,_InIt,_OutIt)' being compiled
1> with
1> [
1> _OutIt=std::unique_ptr<Base,std::default_delete<Base>> *,
1> _Ty=Base,
1> _InIt=std::unique_ptr<Base,std::default_delete<Base>> *
1> ]
1> C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\include\vector(980): note: while compiling class template member function 'std::vector<std::unique_ptr<Base,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>> &std::vector<std::unique_ptr<_Ty,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>>::operator =(const std::vector<std::unique_ptr<_Ty,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>> &)'
1> with
1> [
1> _Ty=Base
1> ]
1> %BASEFOLDER%\include\class.h(33): note: see reference to function template instantiation 'std::vector<std::unique_ptr<Base,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>> &std::vector<std::unique_ptr<_Ty,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>>::operator =(const std::vector<std::unique_ptr<_Ty,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>> &)' being compiled
1> with
1> [
1> _Ty=Base
1> ]
1> %BASEFOLDER%\include\class.h(28): note: see reference to class template instantiation 'std::vector<std::unique_ptr<Base,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>>' being compiled
1> with
1> [
1> _Ty=Base
1> ]
2>------ Rebuild All started: Project: ClassExport, Configuration: Release x64 ------
2> main.cpp
2>%BASEFOLDER%\include\class.h(18): warning C4251: 'Derived::fcn_m': class 'std::function<double (double)>' needs to have dll-interface to be used by clients of class 'Derived'
2> %BASEFOLDER%\include\class.h(18): note: see declaration of 'std::function<double (double)>'
2>%BASEFOLDER%\include\class.h(28): warning C4251: 'Container::elems_m': class 'std::vector<std::unique_ptr<Base,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>>' needs to have dll-interface to be used by clients of class 'Container'
2> with
2> [
2> _Ty=Base
2> ]
2> %BASEFOLDER%\include\class.h(28): note: see declaration of 'std::vector<std::unique_ptr<Base,std::default_delete<_Ty>>,std::allocator<std::unique_ptr<_Ty,std::default_delete<_Ty>>>>'
2> with
2> [
2> _Ty=Base
2> ]
2>LINK : fatal error LNK1181: cannot open input file 'class.lib'
========== Rebuild All: 0 succeeded, 2 failed, 0 skipped ==========
解决方案
我不是 100% 确定我的解释是正确的,但我相信原因是不同的。请注意,问题出在导出类而不是导入类时。如果您删除 dllexport,一切都会好起来的,但您会遇到链接器问题。
原因是您告诉编译器导出所有类函数,并且显然包括默认函数(默认构造函数、默认复制构造函数等)。这里的问题在于operator=
复制构造函数,因为它们不能存在于您的类中,因为std::unique_ptr<Base>
不可复制。解决方案是删除它们,如下所示:
class DLLEXP Container
{
public:
Container();
Container(const Container&) = delete;
Container& operator=(const Container&) = delete;
}
在普通代码(无 DLL)中,只要您尝试使用复制构造函数,就会遇到同样的问题。但在这种情况下,您会尽早收到错误。
另请注意:
- 编译器警告您所有成员等都应该通过 dllexported,只有在您知道自己在做什么时才忽略它
- https://msdn.microsoft.com/en-us/library/81h27t8c.aspx?f=255&MSPPError=-2147217396描述了类的导入/导出行为
- 导出模板和 STL 类是正确的,应该避免,在较旧的 VS 版本上,您可能会遇到非常微妙的运行时错误
- 如果没有任何变化,则 DLL 中没有模板的字节码。您要么必须使用 dllexport 导出专门针对具体类型的模板,要么提供包括实现在内的完整标头。
推荐阅读
- javascript - 世博会文字转语音无法在 iPhone 7 上运行
- python - Python脚本在cmd中运行时不创建文本文件
- python - 如果我需要更多自定义功能,我可以手动编写 HTML 表单吗?
- java - 我无法运行 Eclipse IDE 安装程序
- graphql - Hasura 错误 - GraphQL 错误:在类型中找不到字段“user_id”:'projects_insert_input'
- node.js - 将 CORS 模块添加到单个快速路线不起作用
- r - 为 R 中的每个数据集创建一个高图
- html - 将 HTML 表格放在图标内的工具提示中
- sql - SQL Server:连接和 where 子句
- java - 网页抓取 - 从 Twitch.tv 获取信息