ADA程序实例(一个简单的智能指针实现)

普通的ADA并不含有垃圾收集等托管程序的特性,除非当ADA的目标运行时建立在Java Virtual Machine或.NET系统上。所以从这个对象内存分配角度,ADA和C++基本上是等价的。

ADA提供的语言特性,基本上足以使得ADA能够实现智能指针。当然,是不是有必要在ADA中使用智能指针(考虑ADA常用的思考建模方式),其完善程度(指针的类型,对于OO的支持和对于一般数据的支持)又是另一会儿事。智能指针再智能也不能达到托管程序所能达到内存管理功能(例如简单的引用计数是无法应对孤立环路结构的释放的)。

当然,纯粹展现一下ADA的语言特性,这不失为一个好的例子。

首先是声明(autoptr.ads)。这里显然是一个泛型模块,而其核心类型是指针要处理的对象的类型(private约束是一个很松的约束,尚需查明是不是最松的)。对这个类型可赋予初始化和终止化方法各一。指针实现是指针对象指向一个含引用计数和对象内容的封装对象(Wrapper)。这里比较重要的是封装对象从ada.finalization.controlled继承,这使得能够对其赋值和跨域的状态进行跟踪。其重载函数initialize相当于C++中的无参构造函数,finalize相当于C++中的析构函数,他们分别在对象数据(变量)进域(begin)和出域(end)调用,伴随着变量的诞生和销毁。Adjust比较特殊,也比较关键,它在对象数据被赋值完毕后调用。另外finalize在对象数据被赋值之前也会调用(这个在运行了这个程序才发现)。因为ADA数据赋值永远是针对其直接内容的深拷贝,所以Adjust可用于对收入数据的处理,基本上起到了拷贝构造函数或赋值重载的作用。

spec中提供了一些主要的功能,如所指对象的获取,指针相等的判断的等号重载(根据指针所指对象的一致性而非指针对象本身相同性),以及一个新建空对象的操作。

with Ada.Finalization;

generic
  -- type of the target the pointer is dealing with
  type target_t is private;

  -- handlers invoked on initialization and finalization respectivelys
  target_initialize : access procedure(target : in out target_t) := null;
  target_finalize : access procedure(target : in out target_t) := null;

package autoptr is

  type Pointer is new Ada.Finalization.Controlled with private;

  -- returns the targeted object pointer 'p' points to
  function target(p : Pointer) return target_t;

  -- override of equal sign that returns if two pointers are considered equal
  -- in which case they are pointing to the same wrapper/target
  function "="(left, right : Pointer) return boolean;

  -- creates an new instance of target and returns a pointer that points to it
  function create return Pointer;

  -- returns the number of pointers referencing the target pointer p points to
  function numrefs(p : Pointer) return integer;

private

  -- wrapper that wraps around an instance of target
  type wrapper_t is tagged
    record
      target : target_t;
      reference_counter : integer;
    end record;

  -- type of access to wrapper for pointer to point to wrapper
  type wrapper_access is access all wrapper_t;

  -- data definition of pointer type
  type Pointer is new Ada.Finalization.Controlled with
    record
      wrapper : wrapper_access;
    end record;

  -- initializer 
  overriding procedure Initialize(p : in out Pointer);
  
  -- adjuster that is called after assignment of 'p'
  overriding procedure Adjust(p : in out Pointer);
 
  -- finalizer (destructor) of the pointer type for dealing with referencing
  overriding procedure Finalize(p : in out Pointer);

end autoptr;

知道spec的这些要点,就能完成实现(autoptr.adb),这其中在关键步骤上进行了打印。注意Finalize函数中一开始的指针判断,这从Finalize的特点看是必须的(这反映在执行结果中)。

with Ada.Unchecked_Deallocation;
with ada.text_io; use Ada.text_io;

package body autoptr is

  -- instantiate a wrapper deallocation procedure
  procedure free_wrapper is new Ada.Unchecked_Deallocation
    (Object=> wrapper_t, Name => wrapper_access);

  -- returns the targeted object poiner 'p' points to
  function target(p : Pointer) return target_t is
  begin
    -- wrapper is guaranteed to be available
    -- if not an exception should be thrown by the system for now
    return p.wrapper.target;
  end target;

  -- creates an new instance of target and returns a pointer that points to it
  function create return Pointer is
    p : Pointer;
  begin
    put_line("creating");

    p.wrapper := new wrapper_t;
    put_line(" step 1");
    
    if target_initialize /= null then
      target_initialize(p.wrapper.target);
    end if;
    put_line(" step 2");

    p.wrapper.reference_counter := 1;
    put_line(" step 3");

    put_line("'create' returning");
    return p;
  end create;

  -- override of equal sign that returns if two pointers are considered equal
  -- in which case they are pointing to the same wrapper/target
  function "="(left, right : Pointer) return boolean is
  begin
    return left.wrapper = right.wrapper;
  end "=";
  
  -- returns the number of pointers referencing the target pointer p points tos
  function numrefs(p : Pointer) return integer is
  begin
    if p.wrapper = null then
      return 0;
    end if;

    return p.wrapper.reference_counter;
  end numrefs;
  
  -- private methods
  
  -- finalizes the target and release the allocation
  procedure finalize_wrapper(p : in out wrapper_access) is
  begin
    if target_finalize /= null then
      target_finalize(p.target);
    end if;

    free_wrapper(p);
  end finalize_wrapper;
  
  -- initializer 
  overriding procedure Initialize(p : in out Pointer) is
  begin
    put_line("initializing");
    null;  -- do nothing; what could be done is instantiate a wrapper
    put_line("initialized");
  end Initialize;
  
  -- adjuster that is called after assignment of 'p'
  overriding procedure Adjust(p : in out Pointer) is
  begin
    put_line("adjusting");
    p.wrapper.reference_counter := p.wrapper.reference_counter + 1;
    put_line("adjusted");
  end Adjust;

  overriding procedure Finalize(p : in out Pointer) is
  begin
    put_line("finalizing");
    if p.wrapper = null then
      put_line(" wrapper is null");
      return;
    end if;
 
    p.wrapper.reference_counter := p.wrapper.reference_counter - 1;
    put(" refcount = "); put_line(integer'Image(p.wrapper.reference_counter));
    -- allowing 'less than' is purely for tolerating erroneous condition
    if p.wrapper.reference_counter <= 0 then
      finalize_wrapper(p.wrapper);
    end if;
    put_line("finalized");
  end Finalize;

begin
  null;
end autoptr;

最后是一个演示程序(autoptr_demo.adb),只覆盖了一个简单的创建和赋值,未充分测试。

with Ada.Strings; use Ada.Strings;
with Ada.Strings.Fixed; use Ada.Strings.Fixed;
with Ada.Text_IO; use Ada.Text_IO;
with autoptr;

procedure autoptr_demo is
  type myrec_t is tagged record
    id    : integer;
    name  : string(1..10);
  end record;
  
  id : integer := 1;
  
  procedure myrecinit(myrec : in out myrec_t) is 
  begin
    myrec.id := id;
    id := id + 1;
    myrec.name := 10 * ' ';
    myrec.name := overwrite(myrec.name, 1, "rec");
    myrec.name := overwrite(myrec.name, 4, trim(integer'Image(id), Both));
    put("record{"); 
    put(integer'Image(myrec.id)); put("; '"); put(myrec.name); 
    put_line("'} created");
  end myrecinit;
  
  package myptr is new autoptr(target_t => myrec_t, 
                               target_initialize => myrecinit'Access);
  
  p1, p2 : myptr.Pointer;
  
begin
  p1 := myptr.create;
  put("reference count of p1 is "); put_line(integer'Image(p1.numrefs));
  
  p2 := p1;
  put("reference count of p2 is "); put_line(integer'Image(p2.numrefs));
  
  put("p2.name = '"); put(p2.target.name); put_line("'");
  
end;

运行结果(其中wrapper is null说明在赋值前的Finalize调用作用在了未赋值指针上了):

initializing
initialized
initializing
initialized
initializing
initialized
creating
 step 1
record{ 1; 'rec2      '} created
 step 2
 step 3
'create' returning
adjusting
adjusted
finalizing
 refcount =  1
finalized
finalizing
 wrapper is null
adjusting
adjusted
finalizing
 refcount =  1
finalized
reference count of p1 is  1
finalizing
 wrapper is null
adjusting
adjusted
reference count of p2 is  2
p2.name = 'rec2      '
finalizing
 refcount =  1
finalized
finalizing
 refcount =  0
finalized


posted @ 2011-11-16 22:28  quanben  阅读(452)  评论(0编辑  收藏  举报