c++ - DLL 将抽象基类导出为成员函数的参数 - C++

标签 c++

我想知道如何使用一个成员函数导出一个 DLL(使用 __declspec(dllexport) - 所以,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;
}

执行文件:

主要.cpp

#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,字节码版本 unique_ptr<Base>必须包含在 DLL 中(而不是依赖于它在运行时创建 - 这会发生什么吗?),由于 Base 中的纯虚函数,编译器无法做到这一点.我走在正确的轨道上吗?

我的目标是:

  1. fieldAtS 实现多态性- 也就是说,我希望能够在运行时选择一些字段模型(并且在适当的时候,将多个添加到 Container/对它们求和以获得所有贡献元素的总字段)
  2. 从DLL中导出这些类并在其他地方调用它们
  3. 仍然使用智能指针(在我的代码的其他地方,我导出函数动态创建一个类的实例并传递一个(哑)指针,以及在其他情况下接收哑指针并调用它的成员函数 - 想避免这种情况)。

那么几个问题:

  • 我对正在发生的事情的理解正确吗?
  • 对于必须(也许不一定)的类来说,这是一种好的设计方法吗 dllexport编辑了吗?
  • 我将如何在这里实现我的目标?也就是说,如果这是好的设计,我该如何正确导出容器?如果不是,我还应该如何构建我的代码?

编辑:构建日志/更新信息

似乎错误指的是默认复制构造函数: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 导出专门用于具体类型的模板,要么提供包括实现在内的完整 header 。

关于c++ - DLL 将抽象基类导出为成员函数的参数 - C++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50105907/

相关文章:

c++ - 屏蔽对于阻止旁路攻击是否有效?

c++ - 字符串 <-> 字节 [] 转换

c++ - Const 接收一个 var,我不能将它传递给模板

c++ - 使用 atan2 将 - 1 到 1 的范围转换为度数

c++ - 混合模板函数重载和继承

c++ - 如何使用 C++ 中的 Eigen 库创建高效的 3D 矩阵?

c++ <= 运算符似乎不起作用

c++ - 内联静态成员变量

c++ - 如何区分翻译不存在或翻译与来源相同?

c++ - 从源代码项目中提取所有功能的工具