34.5 继承和聚合

Octave 通过继承和聚合来支持使用类构建新类。

类继承是通过 Octave 在类构造函数中提供的 class 函数实现的。与多项式类的情况类似,Octave 程序员将创建一个包含该类所需数据字段的结构体,然后调用 class 函数来指示要从该结构体创建对象。创建现有对象的子对象是通过创建父类的对象并将该对象作为 class 函数的第三个参数来完成的。

这最容易通过示例来说明。假设程序员需要一个 FIR 滤波器,即分子多项式为某值而分母为 1 的滤波器。在传统的 Octave 编程中,这按如下方式执行。

>> x = [some data vector];
>> n = [some coefficient vector];
>> y = filter (n, 1, x);

等效行为可以实现为一个类 @FIRfilter。此类的构造函数是类目录 @FIRfilter 中的文件 FIRfilter.m

## -*- texinfo -*-
## @deftypefn  {} {} FIRfilter ()
## @deftypefnx {} {} FIRfilter (@var{p})
## Create a FIR filter with polynomial @var{p} as coefficient vector.
## @end deftypefn

function f = FIRfilter (p)

  if (nargin == 0)
    p = @polynomial ([1]);
  elseif (! isa (p, "polynomial"))
    error ("@FIRfilter: P must be a polynomial object");
  endif

  f.polynomial = [];
  f = class (f, "FIRfilter", p);

endfunction

与前述情况类似,前导注释为类构造函数提供文档。此构造函数与多项式类构造函数非常相似,只是将多项式对象作为 class 函数的第三个参数传递,告诉 Octave FIRfilter 类将从多项式类派生。FIR 滤波器类本身没有任何数据字段,但它必须向 class 函数提供一个结构体。由于 @polynomial 构造函数会向对象结构体添加一个名为 polynomial 的元素,@FIRfilter 仅初始化一个带有虚拟字段 polynomial 的结构体,该字段稍后会被覆盖。

请注意,示例代码总是处理不提供参数的情况。这一点很重要,因为 Octave 在从保存的文件加载对象以确定继承结构时,会调用不带参数的构造函数。

一个类可以是多个类的子类(另请参阅 class),并且可以嵌套继承。除了内存或其他物理限制外,父类的数量或嵌套级别没有限制。

对于 FIRfilter 类,需要对对象显示进行更多控制。因此,重写 display 方法而不是 disp 方法(另请参阅 类方法)。一个简单的例子可能是

function display (f)
  printf ("%s.polynomial", inputname (1));
  disp (f.polynomial);
endfunction

请注意,FIRfilter 的显示方法依赖于 polynomial 类的 disp 方法来实际显示滤波器系数。此外,请注意,在 display 方法中,从以下行开始方法是有意义的 printf ("%s =", inputname (1)),以与 Octave 的常规用法保持一致,Octave 打印要显示的变量名后跟值。通常不建议重写 display 函数。

 
display (obj)

显示以对象名称为前缀的对象 obj 的内容。

Octave 解释器在需要将类显示在屏幕上时会调用 display 函数。通常,这将是一个不以分号结尾以抑制输出的语句。例如:

myclass (...)

或者:

myobj = myclass (...)

通常,用户定义的类应该重写 disp 方法以避免默认输出:

myobj = myclass (...)
  ⇒   myobj =

  <class myclass>

当重写 display 方法时,必须注意正确显示对象的名称。这可以通过使用 inputname 函数来实现。

参见: dispclasssubsrefsubsasgn

一旦构造函数和显示方法存在,就可以创建类的实例。也可以检查类类型并检查底层结构体。

octave:1> f = FIRfilter (polynomial ([1 1 1]/3))
f.polynomial = 0.33333 + 0.33333 * X + 0.33333 * X ^ 2
octave:2> class (f)
ans = FIRfilter
octave:3> isa (f, "FIRfilter")
ans =  1
octave:4> isa (f, "polynomial")
ans =  1
octave:5> struct (f)
ans =

  scalar structure containing the fields:

polynomial = 0.33333 + 0.33333 * X + 0.33333 * X ^ 2

使该类可用的唯一方法是处理数据的方法。但在那之前,通常还希望有一种更改存储在类中的数据的方法。由于底层结构体中的字段默认是私有的,因此有必要提供一种访问字段的机制。可以使用 subsref 方法来完成这两个任务。

function r = subsref (f, x)

  switch (x.type)

    case "()"
      n = f.polynomial;
      r = filter (n.poly, 1, x.subs{1});

    case "."
      fld = x.subs;
      if (! strcmp (fld, "polynomial"))
        error ('@FIRfilter/subsref: invalid property "%s"', fld);
      endif
      r = f.polynomial;

    otherwise
      error ("@FIRfilter/subsref: invalid subscript type for FIR filter");

  endswitch

endfunction

() 情况允许我们使用构造函数提供的多项式来过滤数据。

octave:2> f = FIRfilter (polynomial ([1 1 1]/3));
octave:3> x = ones (5,1);
octave:4> y = f(x)
y =

   0.33333
   0.66667
   1.00000
   1.00000
   1.00000

. 情况允许我们查看多项式字段的内容。

octave:1> f = FIRfilter (polynomial ([1 1 1]/3));
octave:2> f.polynomial
ans = 0.33333 + 0.33333 * X + 0.33333 * X ^ 2

为了更改对象的内容,需要 subsasgn 方法。例如,以下代码使多项式字段可公开写入:

function fout = subsasgn (f, index, val)

  switch (index.type)
    case "."
      fld = index.subs;
      if (! strcmp (fld, "polynomial"))
        error ('@FIRfilter/subsasgn: invalid property "%s"', fld);
      endif
      fout = f;
      fout.polynomial = val;

    otherwise
      error ("@FIRfilter/subsasgn: Invalid index type")
  endswitch

endfunction

因此:

octave:1> f = FIRfilter ();
octave:2> f.polynomial = polynomial ([1 2 3])
f.polynomial = 1 + 2 * X + 3 * X ^ 2

FIRfilter 类定义为多项式类的子类意味着 FIRfilter 对象可以在多项式对象可以使用的任何地方使用。这不是滤波器的正常用法。使用聚合而不是继承可能是一种更明智的设计方法。在这种情况下,多项式只是类结构中的一个字段。聚合情况下的类构造函数应该是

## -*- texinfo -*-
## @deftypefn  {} {} FIRfilter ()
## @deftypefnx {} {} FIRfilter (@var{p})
## Create a FIR filter with polynomial @var{p} as coefficient vector.
## @end deftypefn

function f = FIRfilter (p)

  if (nargin == 0)
    f.polynomial = @polynomial ([1]);
  else
    if (! isa (p, "polynomial"))
      error ("@FIRfilter: P must be a polynomial object");
    endif

    f.polynomial = p;
  endif

  f = class (f, "FIRfilter");

endfunction

对于此示例,只有构造函数需要更改,所有其他类方法保持不变。


版权所有 © 2024-2026 Octave中文网

ICP备案/许可证号:黑ICP备2024030411号-4