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 函数。
(obj) ¶显示以对象名称为前缀的对象 obj 的内容。
Octave 解释器在需要将类显示在屏幕上时会调用 display 函数。通常,这将是一个不以分号结尾以抑制输出的语句。例如:
myclass (...)
或者:
myobj = myclass (...)
通常,用户定义的类应该重写 disp 方法以避免默认输出:
myobj = myclass (...) ⇒ myobj = <class myclass>
当重写 display 方法时,必须注意正确显示对象的名称。这可以通过使用 inputname 函数来实现。
一旦构造函数和显示方法存在,就可以创建类的实例。也可以检查类类型并检查底层结构体。
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