MATLAB 面向对象编程OOP在MATLAB中设计用户类.docx
《MATLAB 面向对象编程OOP在MATLAB中设计用户类.docx》由会员分享,可在线阅读,更多相关《MATLAB 面向对象编程OOP在MATLAB中设计用户类.docx(14页珍藏版)》请在冰豆网上搜索。
![MATLAB 面向对象编程OOP在MATLAB中设计用户类.docx](https://file1.bdocx.com/fileroot1/2023-1/29/e4c1bfb9-e5f4-4506-a89e-6114f2fd6aa9/e4c1bfb9-e5f4-4506-a89e-6114f2fd6aa91.gif)
MATLAB面向对象编程OOP在MATLAB中设计用户类
MATLAB面向对象编程(OOP)-在MATLAB中设计用户类
在设计MATLAB类时,应包括一组标准的方法以使类的行为与MATLAB环境协调一致并合乎逻辑。
根据所设计的类的特点,你不一定要包含所有这些方法,并且你可能会包含一些其它方法以实现类的设计目标。
下表所列为MATLAB类中包含的基本方法。
类方法
说明
类构造器
创建类的对象
display
当MATLAB显示对象内容时被调用(例如,当输入的表达式没有以分号结束时)
set和get
访问类的属性
subsref和subsasgn
允许对用户对象进行下标引用和赋值
end
在对象的索引表达式中支持end语法。
subsindex
支持在索引表达式中使用对象
double和char等类型转换函数
将对象转换为MATLAB数据类型的方法
接下来的几节将讨论每种方法的实现并提供本章中所用例子的参考链接。
类构造方法
特定类的@目录中必须包括一个称为该类的构造函数的M-文件。
构造函数的名称与类目录的名称相同(除了@前缀和.m扩展名)。
构造函数通过初始化类数据结构和初始化一个类的对象来创建对象。
构造函数编写指南
类构造函数必须执行某些功能,以使对象在MATLAB环境下正确运行。
通常,一个类的构造函数必须处理三种可能的输入参量组合:
无输入参数
一个同类对象作为输入参数
用来创建类的对象的输入参数(通常是某类数据)
无输入参数。
如果没有输入参数,构造函数应该创建一个默认对象。
既然没有输入,你没有用来创建对象的数据,所以只需用空的或默认值来初始化对象的数据,调用class函数来初始化对象,并将对象作为输出参数返回。
这样做有两方面的原因:
u当加载对象到工作空间时,load函数以无参数的方式调用类的构造函数。
u在创建对象数组时,MATLAB调用此类构造函数来向数组中添加对象。
对象输入参数。
如果参数列表中的第一个参数是一个同类对象,构造函数应该只是将对象返回。
可以用isa函数来确定输入参数是否类的成员。
这一构造函数语法的例子见重载+运算符。
数据输入参数。
如果输入参数存在且不是本类的对象,那么构造函数就用输入参数创建对象。
当然,像在其它函数中一样,你也应在构造函数中对参数进行适当的检查。
典型的做法是用varargin作为输入参数,用一个switch语句来控制程序流程。
这为我们提供了一种可以适应三种情况:
无输入、对象输入、数据输入来创建对象的简单方法。
正是在构造函数的这一部分中,你对对象的数据进行赋值、调用class函数初始化对象并将对象作为输出参数返回。
如果需要,可以用superiorto和inferiorto函数来将对象放置到对象层次结构中。
构造函数中class函数的使用
在构造方法中,你通过class函数来将一个对象结构关联到某特定的类。
这是通过仅可通过class和isa函数访问的内部类标签实现的。
例如,下面class函数的调用将对象p标记为polynom类
p=class(p,’polynom’);
构造方法的例子
有关构造方法的例子请查看下列章节:
Polynom类的构造方法
Asset类的构造方法
Stock类的构造方法
Portfolio类的构造方法
在类目录外识别对象
在类构造方法中使用的class和isa函数同样可以在类目录外使用。
表达式
isa(a,’classname’);
检查a是否为指定类的对象。
例如,如果p是一个polynom对象,下列每个表达式都为真。
isa(pi,’double’);
isa(‘hello’,’char’);
isa(p,’polynom’);
在类目录之外,class函数只接受一个参数(只有在构造函数中,class才可有多于一个参数)。
表达式
class(a)
返回一个包含a的类名的字符串。
例如,
class(pi),
class(‘hello’),
class(p),
返回
‘double’,
‘char’,
‘polynom’
用whos函数可以查看MATLAB工作空间中有哪些对象。
whos
NameSizeBytesClass
p1×1156polynomobject
display方法
只要没有以分号结束的语句的结果是一个对象,MATLAB就会调用名为display的方法。
例如,创建double型的变量a时,MATLAB将调用double的display方法。
a=5
a=
5
你应该定义一个display方法,以使得MATLAB在从你的类引用对象时能够在命令行显示其值。
既然MATLAB以字符串显示输出,在许多类中,display可以仅仅打印变量的名称,然后用char类型转换方法来打印变量内容或值。
你必须定义char方法来将对象的数据转换成字符串。
display方法的例子
有关display方法的例子请查看下列章节:
Polynom类的display方法
Asset类的display方法
Stock类的display方法
Portfolio类的display方法
访问对象数据
你需要为你的类编写可以访问对象数据的方法。
存取方法有多种实现方式,但所有改变对象数据的方法总是接受一个对象作为输入参数并返回一个数据改变后的新对象。
这是必须的,因为MATLAB不支持参数的引用传递(例如,指针)。
函数仅能改变其私有的和临时的对象副本。
因此,要改变一个已存在的对象,你必须创建一个新的,然后替换那个旧的。
接下来的几节将对set,get,subsasgn和subsref方法的实现技术进行更详细的说明。
set和get方法
set和get方法提供了一种在某些场合方便地访问对象数据的方式。
例如,假设你创建了一个MATLAB可以在图中显示箭头的类(可能由已有的MATLAB线和面片对象构成)。
为了保持接口的一致性,你可以定义set和get方法像MATLAB的set和get函数操作内建图形对象那样来操作箭头对象。
set和get是动词,表明了它们执行的操作,但将用户与对象内部隔离了开来。
set和get方法的例子
有关set和get方法的例子请查看下列章节:
Asset类的get方法和Asset类的set方法
Stock类的get方法和Stock类的set方法
属性名方法
除了通常的set方法,你可以编写一个方法来处理对单个属性的赋值。
这种方法的名称与属性名称相同。
例如,如果你定义了一个创建emplyee数据对象的类,可能在employee对象中有一个名为salary的字段。
那么,你可以定义一个名为salary.m的方法,该方法以一个employee对象和一个值作为输入,并返回设置好指定值的对象。
用subsref和subsasgn进行索引引用
用户类在MATLAB中实现新的数据类型。
就像MATLAB内建数据类型那样,通过索引引用来访问对象数据是很有用的。
例如,如果A是一个double类的数组,A(i)可以返回A的第i个元素。
作为类的设计者,你可以决定索引引用对对象的含义。
例如,假设你定义了一个创建多项式对象的类,这些对象包含了多项式的系数。
对多项式对象的索引引用
p(3)
可能返回x3的系数、x=3时多项式的值,或其它的设计预期结果。
通过创建subsref和subsasgn两个类方法,你可以定义类的索引行为。
只要对类的对象进行下标引用或赋值,MATLAB就会调用这些方法。
如果你没有给类定义这些方法,就没有为该类的对象定义索引功能。
通常,对对象的索引规则与对结构数组的索引规则一样。
详情见Structures。
类方法中的行为
如果A是MATLAB基本数据类型之一的数组,那么通过引用索引引用A的某个值会调用MATLAB内建subsref方法。
它不会调用你为那种数据类型重载的任何subsref方法。
例如,如果A是double型的数组,并且在MATLAB路径上有一个@double/subsref方法,语句B=A(I)不会调用此方法,而是调用MATLAB的内建subsref方法。
对用户自定义类同样如此。
只要类方法需要重载的subsref或subsassign功能,它必须通过函数调用来调用重载的方法,而不是用像’()’,’{}’,或’.’之类的操作符。
例如,假设你定义了一个多项式类,类中subsref方法的定义是用下标作为自变量的值来求多项式的值。
所以,对于
p=polynom([10-2-5]);
下面的下标表达式返回的是多项式在x=3和x=4处的值。
p([3,4])
ans=
1651
现在假设你像在某个类方法中使用此属性。
要这么做,你必须直接调用subsref函数:
y=polyval(p,x);
subs.type=‘()’;
subs.subs={x};
y=subsref(p,subs);%此处需要调用subsref
处理下标引用
在赋值语句的右边对对象使用下标或字段标志符,称为下标引用。
在此情况下,MATLAB调用名为subsref的函数。
对象下标引用有三种可能情况-数组索引,元包数组索引和结构字段名:
A(I)
A{I}
A.field
每种情况都会导致MATLAB调用类目录下的subsref方法。
MATLAB传递两个参数给subsref.
B=subsref(A,S)
第一个参数是被引用的对象。
第二个参数S是一个有两个字段的结构数组:
uS.type是一个包含’()’,’{}’,或’.’的字符串,指明下标类型。
圆括号表示数值数组;花括号表示元包数组,点表示结构数组。
uS.subs是一个元包数组或包含实际下标的字符串。
当元包数组包含字符串’:
’时,分号用来表示下标。
例如,表达式
A(1:
2,:
)
使得MATLAB调用subsref(A,S),其中S是一个1×1的结构
S.type=‘()’
S.subs={1:
2,’:
’}
同样,对表达式
A{1:
2}
来说
S.type=‘{}’
S.subs={1:
2}
表达式
A.field
以
S.type=‘.’
S.subs=‘field’
来调用subsref(A,S).
更为复杂的下标表达式是由这些简单的调用构成的。
在此情况下,length(S)为下标的层数。
例如,
A(1,2).name(3:
4)
调用subsref(A,S)时,其中S是一个3×1的结构数组,其值如下:
S
(1).type=‘()’S
(2).type=‘.’S(3).type=‘()’
S
(1).subs={1,2}S
(2).subs=‘name’S(3).subs={3:
4}
如何编写subsref
subsref方法必须对MATLAB传入的下标表达式进行解释。
常用的一种方法是用switch语句来判断索引类型和获取实际下标。
下列3个代码片段例示了如何对输入参数进行解释。
每个例子中,函数必须返回B。
对与数组引用:
switchS.type
case'()'
B=A(S.subs{:
});
end
对元包数组:
switchS.type
case'{}'
B=A(S.subs{:
});%Aisacellarray
end
对结构数组:
switchS.type
case'.'
switchS.subs
case'field1'
B=A.field1;
case'field2'
B=A.field2;
end
end
subsref方法的例子
有关subsref方法的例子请查看下列章节:
Polynom类的subsref方法
Asset类的subsref方法
Stock类的subsref方法
Portfolio类的subsref方法
处理下标赋值
在赋值语句的左边对对象使用下标或字段标志符,称为下标赋值。
在此情况下,MATLAB调用名为subsasgn的函数。
对象下标赋值有三种可能情况-数组索引,元包数组索引和结构字段名:
A(I)=B
A{I}=B
A.field=B
上述每条语句都会以下列格式调用subsasgn
A=subsasgn(A,S,B)
第一个参数,A,是被引用的对象。
第二个参数,S,与subsref中的相应参数具有相同的字段。
第三个参数,B,是新的值。
subsasgn方法的例子
有关subsref方法的例子请查看下列章节:
Asset类的subsasgn方法
Stock类的subsasgn方法
方法中的对象索引
如果在类方法中进行下标引用,MATLAB用其内建的subsref函数来对方法所在类中的数据进行访问。
如果该方法访问其它类中的数据,MATLAB会调用那个类中重载的subsref函数。
对下标赋值和subsasgn来说也是这样。
下例所示为employee类中定义的一个testref方法。
该方法对其所在类的对象的address字段进行引用。
对此,MATLAB使用内建的subsref函数。
testref还对另一个类中的相同字段进行了引用,此时所用的是那个类中重载的subsref。
%----EMPLOYEEclassmethod:
testref.m----
functiontestref(myclass,otherclass)
myclass.address%usebuilt-insubsref
otherclass.address%useoverloadedsubsref
下面创建一个employee对象和一个company对象
empl=employee('Johnson','Chicago');
comp=company('TheMathWorks','Natick');
employee类中中的testref方法被调用。
MATLAB仅在访问方法所在类之外的数据时才调用重载的subsref。
testref(empl,comp)
ans=%built-insubsrefwascalled
Chicago
ans=%@company\subsrefwascalled
Executing@company\subsref...
Natick
定义对象的end索引
当在对象索引表达式中使用end时,MATLAB调用对象的end类方法。
要想在与你的类的对象有关的索引表达式中能够使用end,你必须为类定义一个end方法。
end方法的调用序列为
end(a,k,n)
其中a是用户对象,k是表达式中end所在位置的下标,n是表达式中的总下标数。
例如,表达式
A(end-1,:
)
MATLAB会以下列参数调用对象A中定义的end方法
end(A,1,2)
即,end语句为第一个下标元素,共有2个下标元素。
类方法end必须返回第一维最后一个元素的下标值。
在你的类中实现end方法时,必须保证它返回一个对对象来说合理的值。
用对象来索引对象
当MATLAB碰到下标为一个对象时,它会调用对象中定义的subsindex方法。
例如,假设你有一个对象a,并且你想用此对象来对另一对象b进行索引。
c=b(a);
subsindex方法可能仅仅是简单地将对象转换成double格式然后用其作为下标,如下列代码所示例
functiond=subsindex(a)
%SUBSINDEX
%converttheobjectatodoubleformattobeused
%asanindexinanindexingexpression
d=double(a);
需要注意的是,subindex的值是基于0的,而不是基于1的。
类转换方法(ConverterMethods)
类转换方法是与其它类同名的类方法,例如char或double。
类转换方法接收一个类的对象作为输入并输出另一个类的对象。
类转换方法是你能够
u使用另一个类中定义的方法
u确保包含多个类的对象的表达式能够正确执行。
类转换函数的调用形式如下
b=classname(a)
其中a是classname之外某个类的对象。
在这种情况下,MATLAB会在对象a的类目录中查找名为classname的方法。
如果输入对象已经是classname类,那么MATLAB会调用仅仅返回输入参数的构造函数,
类转换方法的例子
有关类转换方法的例子请查看下列章节:
Polynom类到Double类的转换
Polynom类到Char类的转换
-----------------------------------------------------------------------------------
MATLAB中的类
2008-02-2015:
11
4.4类(class)
类是一种数据类型,与普通的数据类型不同的是类不仅包含数据,还包含对数据的操作,类把数据和数据操作方法封装在一起,作为一个整体参与程序的运行。
类具有课继承性,创建一个新的类的时候,可以在一个基类中添加成员派生出新类。
类的变量和类的实例是不同的,类的实例是动态分配的内存区域,通常称类的实例维“对象”,同一个类可以有不同的实例存在,他们个子有自己的数据,但是数据操作方法是相同的。
类的变量可以看作是一个指针,指向类的实例。
在MATLAB中,为了更好地进行数据的封装,更加抽象地描述对象,也定义了类。
图2.4.1给出了MATLAB中类的层次结构图,在建立类的结构的时候,可以根据这一层次结构进行设计和继承。
4.4.1建立一个类
通常一个类应该包含四个基本的成员函数:
构造函数――与类名相同,可以在其中完成成员初始化的工作;
显示函数――名为display,用于显示成员的数据;
赋值函数――名为set,用于设置类成员的数值;
取值函数――名为get,用于读取类成员的函数。
与C++的类不同的是,MATLAB的类不需要特别的析构函数。
如果类用到了一些特殊的内容需要释放的话,可以编写一个成员函数,比如classclear,用包来释放所占用的资源。
下面用一个简单的例子类介绍一些类如何使用。
定义一个名为list的类,它有两个数据成员x和y,希望通过一个成员函数prod()来获取x和y的乘积。
本例的类是这样的:
类名:
list
成员变量:
x,y
成员函数:
list:
构造函数
display:
显示函数
get:
取值函数
set:
赋值函数
prod:
计算函数
现在就来建立它。
(1)在工作目录上建立一个子目录(以笔者的习惯为例):
cdE:
\MyProgram\matlab7\chap4
mkdir@list
类名前面要加上一个字符'@’,对于这样形式的目录里所有保存的M文件,MATLAB都认为是类的成员函数。
(2)编写5个成员函数的M文件保存在E:
\MyProgram\matlab7\chap4\@list目录下:
%list.m
functiond=list(x,y)
d.x=x;
d.y=y;
d=class(d,'list');
%display.m
functiondisplay(d)
fprintf('listclass:
\n');
fprintf('x=%d\n',d.x);
fprintf('y=%d\n',d.y);
%get.m
functionval=get(d,prop_name)
switchprop_name
case'x'
val=d.x;
case'y'
val=d.y;
otherwise
error([prop_name,'isnotavalidlistproperty']);
end
%set.m
functiond=set(d,varargin)%该函数的定义在后面的章节再详细讲解
argin=varargin;
whilelength(argin)>=2,
prop=argin{1};
val=argin{2};
argin=argin(3:
end);
switchprop
case'x'
d.x=val;
case'y'
d.y=val;
otherwise
error('Assetproperties:
x,y');
end
end
%prod.m
functionz=prod(d)
z=d.x*d.y;
(3)在MATLAB命令行中进行如下操作:
cdE:
\MyProgram\matlab7\chap4
d=list(11,22)
listclass:
x=11
y=22
prod(d)
ans=
242
d=set(d,'x',7);
get(d,'x')
ans=
7
prod(d)
ans=
154
下面对操作步骤中的细节加以详细解释。
1.类的创建
创建一个新的类,首先需要在构造函数中用结果类型struct建立一个结构变量,包含所需要的成员变量。
注意,这里不用考虑成员函数。
然后用class命令将七转换成类,并返回。
class明年的语法很简单:
类变量=class(结构变量,'类名');
这里类名和构造函数名相同。
程序中要创建这个类的新的变量时,只需调用其构造函数,返回的变量就属于这个类的了。
当然,这里的list.m太过简单,没有任何变量检查,使用中还可以根据情况添加一些扩充功能。
2.类成员变量的赋值
本例中,类的成员变量是在构造函数中赋值的。
MATLAB中,类成员变量都是private的,必须在成员函数中访问,因此就不能箱struct那样,用d.x和d.y等形式访问其成员变量了。
不过,这个问题不严重,编个专门的成员函数get就行了。
另外,成员函数的调用有个跟普通的函数一样。
注意在get和set函数中,成员变量是以字符串的形式指定的,要通过字符串比较的办法进行区分,然后对输出变量进行赋值。
本例中的set函数支持同时对两个成员变量赋值,因此用到了不定长输入产生表varargin,例如:
d=set(d,'x',12)%只对其中一个成员变量赋值
listclass:
x=12
y=22
d=set(d,'x',23,'y',34)%同时对两个成员变量赋值
listclass:
x=23
y=34
3.成员变量的显示
成员被变量disp