Ada中循环依赖解决、访问类型使用及多对多关系实现咨询
嗨,我来帮你一步步解决这些Ada里的问题~
一、先解决你遇到的「missing operand」错误
首先,你写的b.booker := access booker;是不符合Ada语法的。在Ada里,要获取对象的访问指针,得用对象属性标记(比如'Access或者'Unchecked_Access),但这里还有个更关键的问题:
你传入的booker是in模式的参数,这意味着它是原对象的一个局部副本,生命周期只在initialize_booking过程执行期间存在。如果直接取它的访问指针,过程结束后这个指针就会变成「悬空指针」(指向已经被销毁的内存),编译器会直接阻止这种危险操作。
修正方案有两种:
直接传递访问类型参数
把参数改成访问类型,这样你传递的本来就是指针,直接赋值即可:-- 在Bookings包的spec里先声明访问类型 package Bookings is type Booking is tagged private; -- 前置声明Passenger类型(解决循环依赖的关键,后面会说) type Passenger_Access is access all Passengers.Passenger; procedure initialize_booking (b : in out Booking; flight : Unbounded_String; booker : Passenger_Access); private type Booking is tagged record ID : Integer; seat : Integer; flight : Unbounded_String; booker : Passenger_Access; end record; nextID : Integer := 1; nextSeat : Integer := 1; end Bookings; -- 包体实现 package body Bookings is procedure initialize_booking (b : in out Booking; flight : Unbounded_String; booker : Passenger_Access) is begin b.ID := nextID; b.seat := nextSeat; b.flight := flight; b.booker := booker; -- 直接赋值访问指针 nextID := nextID + 1; nextSeat := nextSeat + 1; end initialize_booking; end Bookings;在堆上创建Passenger副本
如果必须传值类型的Passenger,可以用new关键字在堆上创建一个永久副本,再把指针赋值给b.booker:procedure initialize_booking (b : in out Booking; flight : Unbounded_String; booker : Passengers.Passenger) is begin b.ID := nextID; b.seat := nextSeat; b.flight := flight; b.booker := new Passengers.Passenger'(booker); -- 堆上创建副本 nextID := nextID + 1; end initialize_booking;注意这种方式需要你手动管理内存,避免内存泄漏。
二、解决Passenger和Booking的循环依赖问题
Ada的包系统对依赖关系要求很严格,两个包互相with对方会直接导致循环依赖错误。常用的解决方法是使用不完整类型+访问类型来解耦:
具体步骤:
在其中一个包的spec里,只声明另一个包的类型是「不完整类型」(不需要知道具体结构),然后用访问类型来引用它。
比如先写Passengers包的spec:package Passengers is type Passenger is tagged private; -- 可以在这里声明Passenger的操作 procedure set_name (p : in out Passenger; name : String); private type Passenger is tagged record Name : Unbounded_String; -- 其他字段 end record; end Passengers;然后在
Bookings包的spec里,with Passengers之后,声明访问类型,而不是直接使用Passengers.Passenger类型:with Passengers; with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; package Bookings is type Booking is tagged private; -- 声明访问类型,指向Passenger对象 type Passenger_Access is access all Passengers.Passenger; procedure initialize_booking (b : in out Booking; flight : Unbounded_String; booker : Passenger_Access); private type Booking is tagged record ID : Integer; seat : Integer; flight : Unbounded_String; booker : Passenger_Access; end record; nextID : Integer := 1; nextSeat : Integer := 1; end Bookings;这样
Bookings包只依赖Passengers的类型声明,而不是完整的包体,循环依赖就被打破了。
三、反向一对多:Passenger存储Booking的访问类型向量
如果要让Passenger记录自己的所有Booking,同样用访问类型+容器来实现,同时继续用前置声明避免循环依赖:
实现示例:
先在
Bookings包的spec里声明类型:with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; package Bookings is type Booking is tagged private; type Booking_Access is access all Booking; -- 声明Booking的操作 procedure set_flight (b : in out Booking; flight : String); private type Booking is tagged record ID : Integer; seat : Integer; flight : Unbounded_String; end record; end Bookings;然后在
Passengers包中使用向量存储Booking访问指针:with Bookings; with Ada.Containers.Vectors; with Ada.Strings.Unbounded; use Ada.Strings.Unbounded; package Passengers is type Passenger is tagged private; -- 引入Booking的访问类型 type Booking_Access is access all Bookings.Booking; -- 定义存储Booking指针的向量 package Booking_Vectors is new Ada.Containers.Vectors (Index_Type => Positive, Element_Type => Booking_Access); use Booking_Vectors; procedure add_booking (p : in out Passenger; b : Booking_Access); procedure set_name (p : in out Passenger; name : String); private type Passenger is tagged record Name : Unbounded_String; Bookings : Booking_Vectors.Vector; -- 存储关联的Booking指针 end record; end Passengers; -- Passengers包体实现 package body Passengers is procedure add_booking (p : in out Passenger; b : Booking_Access) is begin p.Bookings.Append(b); end add_booking; procedure set_name (p : in out Passenger; name : String) is begin p.Name := To_Unbounded_String(name); end set_name; end Passengers;这里要注意:当Booking对象被销毁时,需要手动从Passenger的向量中移除对应的指针,否则会出现悬空指针问题。如果用Ada 2012及以后的版本,可以考虑用
Ada.Containers.Indefinite_Holders来自动管理生命周期。
四、多对多关系的实现
多对多关系(一个乘客可以订多个航班,一个航班可以被多个乘客订)需要一个中间关联实体来存储两者的关联关系,这样可以避免两个包之间的直接循环依赖:
实现示例:
创建一个专门的关联包
Passenger_Booking_Links:with Passengers; with Bookings; with Ada.Containers.Vectors; package Passenger_Booking_Links is -- 关联实体,存储乘客和预订的指针 type Association is record Passenger : access Passengers.Passenger; Booking : access Bookings.Booking; -- 可以添加额外字段,比如预订时间、状态等 end record; -- 定义存储关联的向量 package Association_Vectors is new Ada.Containers.Vectors (Index_Type => Positive, Element_Type => Association); use Association_Vectors; type Association_List is new Association_Vectors.Vector with null record; -- 关联操作 procedure link_passenger_booking (List : in out Association_List; P : access Passengers.Passenger; B : access Bookings.Booking); -- 取消关联操作 procedure unlink_passenger_booking (List : in out Association_List; P : access Passengers.Passenger; B : access Bookings.Booking); -- 查询方法:获取某个乘客的所有预订 function get_passenger_bookings (List : Association_List; P : access Passengers.Passenger) return Association_Vectors.Vector; end Passenger_Booking_Links;包体实现:
package body Passenger_Booking_Links is procedure link_passenger_booking (List : in out Association_List; P : access Passengers.Passenger; B : access Bookings.Booking) is begin List.Append((Passenger => P, Booking => B)); end link_passenger_booking; procedure unlink_passenger_booking (List : in out Association_List; P : access Passengers.Passenger; B : access Bookings.Booking) is Cursor : Association_Vectors.Cursor := List.First; begin while Association_Vectors.Has_Element(Cursor) loop if Association_Vectors.Element(Cursor).Passenger = P and Association_Vectors.Element(Cursor).Booking = B then List.Delete(Cursor); exit; end if; Association_Vectors.Next(Cursor); end loop; end unlink_passenger_booking; function get_passenger_bookings (List : Association_List; P : access Passengers.Passenger) return Association_Vectors.Vector is Result : Association_Vectors.Vector; Cursor : Association_Vectors.Cursor := List.First; begin while Association_Vectors.Has_Element(Cursor) loop if Association_Vectors.Element(Cursor).Passenger = P then Result.Append(Association_Vectors.Element(Cursor)); end if; Association_Vectors.Next(Cursor); end loop; return Result; end get_passenger_bookings; end Passenger_Booking_Links;这种方式把关联逻辑抽离到单独的包中,既解耦了Passenger和Booking,又能清晰管理多对多关系。
备注:内容来源于stack exchange,提问作者Royal Mail

