如何从C++释放Ada堆分配Tagged Record?遇双释放错误
从C++释放Ada堆分配Tagged Record时的double free错误解决方案
问题概述
运行代码时触发以下错误:
20 double free or corruption (out) raised PROGRAM_ERROR : unhandled signal
目标是构建插件架构,以Ada动态库作为特定类型工厂(类似boost.dll),需解决跨语言释放Ada堆分配Tagged Record的问题,确认是否必须依赖Ada的释放机制。
代码示例
main.cpp
#include <iostream> #include "animal.h" extern "C" { void adainit (void); void adafinal (void); Animal* new_animal(); void del_animal(Animal *); } int main(void) { adainit(); Animal* A = new_animal(); std::cout << A->age() << std::endl; //delete A; del_animal(A); adafinal(); return 0; };
alib.ads
with Interfaces.C; package ALib is type Animal is tagged record The_Age : Interfaces.C.int; end record; pragma Convention (CPP, Animal); type Animal_Class_Access is access Animal'Class; function New_Animal return access Animal'Class; pragma Export(CPP, New_Animal); procedure Del_Animal (this : in out Animal_Class_Access); pragma Export(CPP, Del_Animal); function Age(X : Animal) return Interfaces.C.int; pragma Export(CPP, Age); end ALib;
alib.adb
with ada.unchecked_deallocation; package body ALib is function New_Animal return access Animal'Class is begin return new Animal'(The_Age => 20); end New_Animal; procedure Del_Animal (this : in out Animal_Class_Access) is procedure Free is new ada.unchecked_deallocation(Animal'Class, Animal_Class_Access); begin Free(this); --null; end Del_Animal; function Age(X : Animal) return Interfaces.C.int is begin return X.The_Age; end Age; end ALib;
已尝试操作
- 为创建Free过程测试多种类型与访问类型组合:
Animal+type Animal_Access is access AnimalAnimal'Class+type Animal_Class_Access is access Animal'ClassAnimal+type Animal_Access is access Animal'Class
- 考虑使用
System.Address作为New_Animal返回值和Del_Animal参数
解决方案
必须使用Ada的内存释放机制,核心原因是Ada与C的内存分配器独立,且Ada类访问类型(Animal'Class)的内存布局与C对象不兼容,跨语言直接释放必然触发内存错误。
具体修改步骤
修正Ada端接口类型匹配
将Ada的访问类型改为与C++指针兼容的类型,显式指定导出名称避免命名规则冲突:with Interfaces.C; package ALib is type Animal is tagged record The_Age : Interfaces.C.int; end record; pragma Convention (CPP, Animal); type Animal_Access is access Animal; pragma Convention (CPP, Animal_Access); -- 确保与C++指针布局兼容 function New_Animal return Animal_Access; pragma Export(CPP, New_Animal, "new_animal"); procedure Del_Animal (This : Animal_Access); pragma Export(CPP, Del_Animal, "del_animal"); function Age(X : Animal) return Interfaces.C.int; pragma Export(CPP, Age, "age"); end ALib;修正Ada端释放逻辑
使用匹配的访问类型创建Unchecked_Deallocation过程,确保内存释放正确:with Ada.Unchecked_Deallocation; package body ALib is function New_Animal return Animal_Access is begin return new Animal'(The_Age => 20); end New_Animal; procedure Free_Animal is new Ada.Unchecked_Deallocation(Animal, Animal_Access); procedure Del_Animal (This : Animal_Access) is Temp : Animal_Access := This; begin Free_Animal(Temp); end Del_Animal; function Age(X : Animal) return Interfaces.C.int is begin return X.The_Age; end Age; end ALib;C++端保持接口一致性
确保animal.h的声明与Ada导出接口匹配:class Animal { public: int age() { // 对应Ada导出的age函数 extern "C" int age(Animal&); return age(*this); } };
插件架构最佳实践
- 所有Ada堆分配对象,必须通过Ada导出的释放函数清理,禁止在C++中直接操作内存。
- 多态插件类型建议用
System.Address传递句柄,在Ada内部转换为类访问类型,兼顾类型安全与跨语言兼容性。
内容的提问来源于stack exchange,提问作者shakeysailing
相关产品推荐
相关产品推荐

