关于 类设计 的一个迷惑 200分谢(200分)

  • 主题发起人 主题发起人 mr0511
  • 开始时间 开始时间
M

mr0511

Unregistered / Unconfirmed
GUEST, unregistred user!
请帮我分析一下情况的不同之处。

父类中构造的函数
constructor TFather.Create(TheLogFileName: String);
子类有构造函数
constructor TSon.Create(TheLogFileName: String);

情形1: 父类造函数跟virtual关键字, 子类造函数跟override
情形2: 父类造函数跟virtual关键字, 子类造函数不跟任何关键字

情形3: 父类造函数不跟任何关键字, 子类造函数不跟不跟任何关键字

上面三种情形的子类构造函数的实现中, 第一句都是
inherited Create(TheLogFileName);

上面三种情形,编译都可以通过,只不过情形2有一个编译警告:
[Warning] ToriTCPUnit.pas(38): Method 'Create' hides virtual method of base type
上面三种情形的子类中的inherited Create(TheLogFileName);可被调用,是不是可以认为三种情形执行结果也一样呢?

请从类的设计角度分析,这三种情形有什么差别。谢谢!
 
virtual表示虚函数吧,不可以创建实例的
 
从类设计的角度就是看你想不想在构造子类的时候也初始化基类了.如果想的话就加上
inherited Create(TheLogFileName);
否则的话就不加.
基类有VIRTUAL,子类加上OVERRIDE就FU盖了基类的构造函数了.也就是说在子类的VMT(虚方法表)中,
已经用子类的构造函数代替基类的了.
 
TO _Murray :
>>如果想的话就加上 inherited Create(TheLogFileName);

我说的三种情形都有 inherited Create(TheLogFileName);

我就是想问我描述的三种情形从类的设计角度分析有什么区别。
 
我来告诉你吧
1,3 不用说了,你应该知道吧

对于 2 ,给你一个例子看看

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;

type
Tp=class
public
ss:string;
procedure s;virtual;
constructor create(a:string);
end;
tc= class(tp)
public
procedure s;override;
constructor create(a:string);
end;
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private

public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

constructor tp.create(a:string);
begin
ss := a;
end;

procedure Tp.s;
begin
showmessage('tp');
end;
constructor tc.create(a:string);
begin
ss:=a;
end;
procedure tc.s;
begin
showmessage('tc');
end;

procedure TForm1.Button1Click(Sender: TObject);
var
p:tp;
begin
//p := tp.create('sss');
p := tc.create('sss');
p.s;
p.Free;
end;

end.

用 构造函数不容易说,所以我就用了一个 procedure s;virtual;来说明
tp 是父类 tc 是子类

关键是

procedure TForm1.Button1Click(Sender: TObject);
var
p:tp
// 这里声明的是 tp 父类
begin
//p := tp.create('sss');
p := tc.create('sss')
//我用子类来创建实例
p.s
// 看看 这里显示的是 tp 还是 tc
p.Free;
end;

关于显示的是 tp 还是 tc 要看 tc.s 的定义关键字 如果什么没有,像你说的那样
那 p.s 显示的是 tp ,因为 p:tp 这句,如果 tc.s 是 override ,那 p.s 是
tc
呵呵,也就是 说子类定义了和父类同名的方法时,如果不加任何关键字 override,则与该对象的声明有关,加了 override 则和创建实例有关了

明白了没有?

明白了就给俺分吧 ^_^
 
不好意思,刚才打错了.我是说如果你想在初始化子类的时候也初始化基类的时候,那么就加上inherited Create(TheLogFileName);这句.

如果从类的设计角度来说的话,各种情况不同.比如说TBUTTON就OVERRIDE了基类的,而
TMEMO没有,而是在TCustomMemo中进行的OVERRIDE,也就是说根据需要来选择什么时候
OVERRIDE.
我不是专家,只是自己写过几个控件有所体会,简单说一下而已.^^
 
其实我最开始迷惑的是:我以为inherited和[virtual/override]有关。

根据qi_jianzhou的例子,我现在认为:
inherited与[virtual/override]无关,inherited总是可以调用父类的方法。
至于virtual与override,则与实例的具体声明类型有关,从而决定是调用父类还是子类的方法。
子类override父类的virtual方法后,依然是virtual方法,子类的子类一样可以override
 
构造/析构函数是特例
其它函数肯定要用virtual等关键字,否则会被直接覆盖
 
to 轻舞肥羊:
如何确切地理解你说的“被直接覆盖”
 
直接覆盖这个说法不对,应该是被静态编译了
ta = class
public
procedure a;//virtual;
end;
tb = class(ta)
public
procedure a;//override;
end;

{ ta }
procedure ta.a;
begin
ShowMessage('ta');
end;

{ tb }
procedure tb.a;
begin
ShowMessage('tb');
inherited a;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
a : ta;
begin
a := tb.Create;
try
a.a;//如果不用virtual,这里会显示ta
finally
a.Free;
end;
end;
不用virtual则不会将函数指针存在类的虚方法表(VMT)中,而是直接编译成调用静态地址过程。
 
TO:qi_jianzhou:

讲的是以基本的指针指向派生类的对象,和你说的这个问题不太一样.
 
父类中构造的函数
constructor TFather.Create(TheLogFileName: String);
子类有构造函数
constructor TSon.Create(TheLogFileName: String);

其实是你没有深入的了解面像对象原理才会迷惑。

情形1: 父类造函数跟virtual关键字, 子类造函数跟override
//这个情况是虚拟方法,即字类实现对象后时会调用自已的方法。如果在方法的实现部分多了一个inherited,就会调用到父方法。
情形2: 父类造函数跟virtual关键字, 子类造函数不跟任何关键字
//这个情况在编译器会自动实现override
情形3: 父类造函数不跟任何关键字, 子类造函数不跟不跟任何关键字
//这种情况是静态的实现方法。
有机会是看看高手突破这本书。。里面说得很好。
 
To _Murray

哪里不一样了,我觉得一样呀,他的是构造和析构,而我的是过程,就这点区别吧
其实他的 情景 2 加 reintroduce ,编译器请不提示了呀

关于你说的 以基本的提针指向派生类的对象,有点不太明白,请指教
 
To:hzjone
情形2: 父类造函数跟virtual关键字, 子类造函数不跟任何关键字
//这个情况在编译器会自动实现override
这个不可能吧,我试过了,什么也不加会以对象的实际类型为准
刘艺的 delphi 面向对象一书中说得很明白的,具体在哪页我忘了
 
你写的例程上TP是基类,TC是子类.
p := tc.create('sss');就是用一个基类的指针指向一个派生类的对象,这样主要是为了程序的多态性考虑而做的.

其实这也是多态性的一个表现,不过和楼主说的不是一个方面.
他只是对inheirted 不太清楚.
 
这三种情况如果都是直接声明并调用TSon.Create,那结果是不会有区别的
但是如果以下面这种方式调用 那区别就大了。在Create方法上说不清。按成另一个方法
假设叫 pro1;
TFather.pro1;
TSon.pro1;

Var
obj:TFather
begin
obj:=TSon.Create;
obj.pro1;
第一种情况
TFather.pro1;virtual;
TSon.pro1;override;
obj.pro1;调用 的是TSon.pro1,
TFather(obj).proc1调用的是TSon.pro1
TSon(obj).proc1,调用的还是TSon.pro1
第二部情况
TFather.pro1;virtual;
TSon.pro1;
obj.pro1;调用的是TFather.pro1
TFather(obj).proc1调用的是TFather.pro1
TSon(obj).proc1,调用的是TSon.Proc1;
第三种情况
TFather.pro1;
TSon.pro1;
obj.pro1;调用的是TFather.pro1
TFather(obj).proc1调用的是TFather.pro1
TSon(obj).proc1,调用的是TSon.Proc1;

第一种情况就是多态,它把Tfath和TSon的方法都在VMT表里。当调用时,它根据VMT表查找实际方法,而和声明的变量类型无关,调用的是实际创建的TSon.proc1

第二和第三种情况就是子类覆盖了父类的方法,两个proc1之间各自独立,当你声明为何种对象,访问的就是何种对象的方法。可以通过强制转型访问父类的方法(创建的是子类实例)
 
To:_Murray

上面三种情形,编译都可以通过,只不过情形2有一个编译警告:
[Warning] ToriTCPUnit.pas(38): Method 'Create' hides virtual method of base type
上面三种情形的子类中的inherited Create(TheLogFileName);可被调用,是不是可以认为三种情形执行结果也一样呢?

请从类的设计角度分析,这三种情形有什么差别。谢谢!


我说的和他提的这三种情形有什么差别没有区别吧 ,我的正好解决了他对于 情形 2 的不明白, 1,3 ,很简单,不用说也明白的
 
1 实现多态 如:
var 子类:父类;
子类:=父类.Create(...);//调用的是子类的Create;

2 子类覆盖父类的Create方法。
var 子类:父类;
子类:=父类.Create(...);//调用的是父类的Create;

3 同2不过内存中没有VMT.
 
To hzjone:
我不同意你对第2种情形的解释
 
好象在《OBJECT PASCAL 参考手册》有一段专门讲这个,哦
 

Similar threads

后退
顶部