【问题标题】:C++ : When do I need a shared memory allocator for std::vector?C++:我什么时候需要 std::vector 的共享内存分配器?
【发布时间】:2009-12-06 00:19:10
【问题描述】:

第一层

我有一个用 VC++6 service pack 6 编写的 win32 dll。让我们将此 dll 称为 FirstLayer。我无权访问 FirstLayer 的源代码,但我需要从托管代码中调用它。问题是 FirstLayer 大量使用 std::vector 和 std::string 作为函数参数,并且无法将这些类型直接编组到 C# 应用程序中。

第二层

我能想到的解决方案是先创建另一个用VC++6 service pack 6编写的win32 dll。我们把这个dll称为“SecondLayer”。 SecondLayer 充当 FirstLayer 的包装器。该层包含 std::vector 的包装类,因此 std::vector 不会在该层的所有函数参数中公开。我们将 std::vector 的这个包装类称为 StdVectorWrapper。

该层不使用任何新的或删除的操作来分配或释放内存,因为这是由 std::vector 内部处理的。

第三层

我还创建了一个 VC++2005 类库作为 SecondLayer 的包装器。这个包装器完成了将非托管 SecondLayer 转换为托管代码的所有繁琐工作。让我们将此层称为“ThirdLayer”。

与SecondLayer类似,该层在处理StdVectorWrapper时不使用new和delete。

第四层

最重要的是,我创建了一个 C#2005 控制台应用程序来调用 ThirdLayer。我们将这个 C# 控制台应用程序称为“FourthLayer”。

调用序列摘要

FourthLayer(C#2005) -> ThirdLayer(VC++2005) -> SecondLayer(VC++6) -> FirstLayer(VC++6)

问题

我注意到“System.AccessViolationException: Attempted to read or write protected memory”异常被抛出,我怀疑这是由于 SecondLayer 的内部 std::vector 分配内存是非法的第三层访问。

我认为这得到了证实,因为当我在 VC++2005 中重新编译 FirstLayer(模拟)和 SecondLayer 时,问题完全消失了。但是,由于我没有源代码,无法重新编译 FirstLayer 的生产版本。

我听说为了解决这个问题,我需要用 C++ 为 SecondLayer 的 std::vector 编写一个共享内存分配器,它位于 StdVectorWrapper 类中。我不完全理解为什么我需要一个共享内存分配器以及它是如何工作的?有什么想法吗?

互联网上是否有任何现成的源代码可以编译并与我在 SecondLayer 中的代码一起使用?

请注意,我无法为此使用 boost 库。

【问题讨论】:

  • 忘记我的第一个答案。这是错误的。

标签: c++ memory dll stl shared-memory


【解决方案1】:

每个可执行文件或 dll 链接到特定版本的 c 运行时库,其中包含 newdelete 的实现。如果两个模块具有不同的编译器(VC2005 与 VC6)或构建设置(调试与发布)或其他设置(多线程运行时与非多线程运行时),那么它们将链接到不同的 c 运行时。如果一个运行时分配的内存被另一个运行时释放,这将成为一个问题。

现在,如果我没记错的话,模板(例如 std::vector 或 std::string)可能会导致这个问题潜入不明显的地方。问题在于模板是单独编译到每个模块中的。

示例:模块 1 使用一个向量(从而分配内存),然后将其作为函数参数传递给模块 2,然后模块 2 操作该向量导致内存被释放。在这种情况下,内存是使用模块 1 的运行时分配的,并使用模块 2 的运行时释放。如果这些运行时不同,那么您就有问题了。

因此,考虑到所有这些,您有两个地方可以解决潜在问题。如果这两个模块没有使用 exact 相同的设置编译,则一个位于 FirstLayer 和 SecondLayer 之间。另一个在 SecondLayer 和 ThirdLayer 之间,如果 任何 内存在其中一个分配并在另一个中释放。

你可以再写几个测试程序来确认哪些地方有问题。

要测试 FirstLayer-SecondLayer,请将 SecondLayer 函数的实现复制到 VC6 程序中,编写足够的代码以以典型方式调用这些函数,并仅链接 FirstLayer。

如果第一个测试没有失败,或者一旦你修复它,然后测试 SecondLayer-ThirdLayer:将 ThirdLayer 实现复制到 VC2005 程序中,编写代码以进行典型调用,然后链接到 SecondLayer。

【讨论】:

    【解决方案2】:

    我认为你应该看看不同的解决方案架构。

    我相信,VC6 stl 向量和字符串生成的二进制代码与最新版本的 VC 生成的代码不同,因为许多 stl 升级。因此,我认为您的架构不会工作,因为 dll 将具有两个不兼容二进制的 std::vector 和 std::string 实现。

    我建议的解决方案是回到 VC6 并为 FirstLayer 编写一个新的包装 dll,通过纯 C API 公开它——这一层需要使用 VC6sp6 进行编译,以确保它与 FirstLayer 二进制兼容。然后使用 Fourth_Layer 中的 PInvoke 访问 VC6 包装 DLL。

    【讨论】:

      【解决方案3】:

      我已经找到了解决问题的方法。基本上,我写的 StdVectorWrapper 类没有实现深拷贝。所以我需要做的就是在 StdVectorWrapper 类中添加以下内容来实现深拷贝。

      • 复制构造函数
      • 赋值运算符
      • 解构器

      编辑:替代解决方案

      更好的解决方案是对 std::vector 中包含的所有元素以及 std::vector 本身使用clone_ptr。这消除了对复制构造函数、赋值运算符和解构函数的需要。

      【讨论】:

        猜你喜欢
        • 2011-07-15
        • 1970-01-01
        • 2015-04-16
        • 2022-11-28
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多