C++ DLL 堆内存分配问题

C++ dll heap memory allocation issue

本文关键字:分配 问题 内存 DLL C++      更新时间:2023-10-16

从这个链接中,我知道我们(应用程序(应该始终不要从dll中删除堆内存分配,因为堆内存管理器是不同的。

我有几个问题:

1.(.so文件(Linux(怎么样,情况是一样的吗?

2.( 有没有办法确保应用程序和库(.dll 和 .so(使用相同的堆内存管理器或使用相同的堆内存部分?因此,我们可以分别删除和新建(在 .dll/.so 处新建,在应用程序处删除(。

谢谢。

1.( .so 文件(Linux(怎么样,情况是一样的吗?

是的,使用与最终链接的程序不同的标准C++库实现构建的库可能会以略有不同的方式进行分配。g++libstdc++clang++libc++是两种不同实现的例子。它们可能是100% ABI 兼容的 - 但第三个未知库可能不是。

2.( 是否有办法确保应用程序和库(.dll 和 .so(使用相同的堆内存管理器或使用相同的堆内存部分?因此,我们可以分别删除和新建(在 .dll/.so 处新建,在应用程序处删除(。

不,编译到库中的内容是库将使用的内容,除非有办法在加载库时初始化库,告诉它使用特定的堆管理器。

请详细解释。我想知道 .so(Linux(,它是否只对应用程序和 .so(库(使用一个堆管理器。假设,我的应用程序由编译器版本 A 编译,而我的 .so 由编译器版本 B 编译,它仍然可以吗?

由于上述原因,不,您无法确定。但是,由于您是库创建者,因此您可以制作 API,以便将库中类型的newdelete内存分配/解除分配委托给编译到库中的成员函数,而成员函数又执行实际分配/解除分配(在运算符 new、运算符 new[] 中描述。 和运算符删除,运算符删除[](。然后,可以在库和应用程序之间new和传递指向类型对象的指针,并在两端delete


下面是一个(不完整的(示例,说明使用特定于类的分配函数可能看起来如何:
void* T::operator new(std::size_t count);

以及特定于类的常用释放函数
void T::operator delete(void* ptr);

它包含foo.hppfoo.cpp用于创建libfoo.so(或libfoo.a(和代码的程序使用库。

foo.hpp

#pragma once
#include <new>
class Foo {
public:
// The "usual" part of your class definition:
Foo(int x);
~Foo();
// This part does NOT get compiled into your library.
// It'll only be used by users of your library:
#ifndef BUILDING_LIB
// Note: operator new and delete are static by default
// single object allocation/deallocation:
void* operator new(std::size_t /* byte_count */) { return Alloc(); }
void operator delete(void* addr) { Free(addr); }
// array allocation/deallocation:
// TODO: operator new[] and delete[]
#endif
private:
int value;
// the functions really doing the memory management
static void* Alloc();
static void Free(void* p);
};

傅.cpp

// Define BUILDING_LIB to disable the proxy operator new/delete functions when building
// the library.
#define BUILDING_LIB
#include "foo.hpp"
#include <cstdlib> // std::aligned_alloc
#include <iostream>
Foo::Foo(int x) : value(x) {
std::cout << "Foo:Foo(" << value << ")n";
}
Foo::~Foo() {
std::cout << "Foo:~Foo() " << value << "n";
}
void* Foo::Alloc() {
void* addr = std::aligned_alloc(alignof(Foo), sizeof(Foo));
std::cout << "Alloc() " << sizeof(Foo) << "t@ " << addr << 'n';
return addr;
}
void Foo::Free(void* addr) {
std::cout << "Free()tt@ " << addr << 'n';
std::free(addr);
}

使用库.cpp

#include "foo.hpp"
#include <memory>
int main() {
auto a = std::make_unique<Foo>(123); // heap allocation
// An automatic variable will use the applications memory manager and will not
// use Alloc/Free.
Foo b(456);
}

可能的输出:

Alloc() 4       @ 0x1af7eb0
Foo:Foo(123)
Foo:Foo(456)
Foo:~Foo() 456
Foo:~Foo() 123
Free()          @ 0x1af7eb0

1(符号表在Linux的整个过程中共享。 malloc(( 表示 过程与所有其他部分相同。所以是的,如果流程的所有部分都访问 堆通过 malloc(( et alia 然后它们将共享相同的堆。

2(但是第二个问题我也有点困惑。

堆管理器位于运行时库的静态内存中(Win 的 msvcrXXX 和 Linux 的 crt(。您可以通过两种方式使用运行时库:静态或动态。如果将运行时作为静态链接到自己的库,则无法在库中分配并在另一个库中释放。但是您将运行时链接为动态,然后您可以在库中分配并在另一个库中释放。

库开发人员通过实现自己的方法来释放库内分配的内存来解决这个问题。 例如:

  • OpenIPMI调用ipmi_fru_node_get_fieldipmi_fru_data_free
  • CryptoAPI 调用CertCreateCertificateContextCertFreeCertificateContext