Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / fpc-pascal / 2013-08

Understanding virtual methods

14 messages · 5 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Xiangrong Fang

Hi All,

I am reading this document:
http://www.freepascal.org/docs-html/ref/refsu29.html and doing an experiment with the following code:

program project1;
{$mode objfpc}{$H+}
type
  TBase = class
    constructor Create; virtual;
  end;
  TDerived = class(TBase)
    constructor Create; override;
  end;
var
  c1, c2: TBase;
  c3: TDerived;
constructor TDerived.Create;
begin
  WriteLn('Entering TDerived.Create');
  inherited Create;
  WriteLn('Leaving TDerived.Create');
end;
constructor TBase.Create;
begin
  WriteLn('Entering TBase.Create');
  Writeln('Leaving TBase.Create');
end;
begin
  WriteLn('Creating a TBase and assigning to TBase variable...');
  c1 := TBase.Create;
  WriteLn('Creating a TDerived and assigning to TBase variable...');
  c2 := TDerived.Create;
  WriteLn('Creating a TDerived and assigning to TDerived variable...');
  c3 := TDerived.Create;
end.

The problem is, it makes NO DIFFERENCE at all in the following cases:

CASE 1:

TBase.Create;
TDerived.Create;

CASE 2:

TBase.Create; virtual;
TDerived.Create; virtual;

CASE 3:

TBase.Create; virtual;
TDerived.Create; override;

According to the document, "inherited" cannot be used in non-virtual methods, and it is wrong to use virtual in sub-class. But my test shows the contrary. BTW, I am running Linux on 64bit platform.

Regards,
Shannon

Reply by email

Flávio Etrusco
to Xiangrong Fang
Xiangrong Fang wrote 62 lines
Hi All, I am reading this document: http://www.freepascal.org/docs-html/ref/refsu29.html and doing an experiment with the following code: program project1; {$mode objfpc}{$H+} type TBase = class constructor Create; virtual; end; TDerived = class(TBase) constructor Create; override; end; var c1, c2: TBase; c3: TDerived; constructor TDerived.Create; begin WriteLn('Entering TDerived.Create'); inherited Create; WriteLn('Leaving TDerived.Create'); end; constructor TBase.Create; begin WriteLn('Entering TBase.Create'); Writeln('Leaving TBase.Create'); end; begin WriteLn('Creating a TBase and assigning to TBase variable...'); c1 := TBase.Create; WriteLn('Creating a TDerived and assigning to TBase variable...'); c2 := TDerived.Create; WriteLn('Creating a TDerived and assigning to TDerived variable...'); c3 := TDerived.Create; end. The problem is, it makes NO DIFFERENCE at all in the following cases: CASE 1: TBase.Create; TDerived.Create; CASE 2: TBase.Create; virtual; TDerived.Create; virtual; CASE 3: TBase.Create; virtual; TDerived.Create; override; According to the document, "inherited" cannot be used in non-virtual methods,

At least that's the result I would expect :)
I can confirm your findings; contrary to the documentation, a simple test works correctly (the compiler prints no warnings or hints, both method and constructor work correctly, calling either a non-virtual from virtual, and virtual from non-virtual).

and it is wrong to use virtual in sub-class.

It doesn't say that, it simply says that the redeclaration will not override the base implementation (the different behavior for 'object' is news to me!).

FWIW your test doesn't actually "exercise" inheritance, since virtual constructors only make a difference when you call from a class reference (similarly, you'll only see a difference in virtual methods when calling from a base-typed variable).

Code>>>
var MyClass: TComponentClass = TDataModule;
begin
  MyClass.Create(nil);
<<<

will instantiate a TDataModule.

Code>>>
var MyControl: TControl;
begin
  MyControl := TButton.Create;
  MyControl.ExecuteDefaultAction;
<<<

will invoke TButton's implementation of ExecuteDefaultAction.

But my test shows the contrary. BTW, I am running Linux on 64bit platform. Regards, Shannon

Best regards,
Flávio

Reply by email

Xiangrong Fang
to Flávio Etrusco

Hi Flavio,

Your findings confirmed mine, but not telling me why? It seems that the "virtual" keyword has no use at all! To confirm this, I just removed the "inherited" call in TDerived, then re-run the program with or without "virtual/override", the result is exactly same, i.e. with c2 (declared as TBase), the following statements ALWAYS calls constructor of TDerived, NOT TBase:

c2 := TDerived.Create;
c2 := TBase(TDerived.Create);

This is not same as the description in:
http://www.freepascal.org/docs-html/ref/refsu26.html

BTW, the above documents are talking about objects, but I am using classes, is there any difference here?

Shannon

2013/8/20 Flávio Etrusco

Show quoted text 111 lines
On Mon, Aug 19, 2013 at 10:44 PM, Xiangrong Fang wrote: > Hi All, > > I am reading this document: > http://www.freepascal.org/docs-html/ref/refsu29.html and doing an > experiment with the following code: > > program project1; > {$mode objfpc}{$H+} > type > TBase = class > constructor Create; virtual; > end; > TDerived = class(TBase) > constructor Create; override; > end; > > var > c1, c2: TBase; > c3: TDerived; > > constructor TDerived.Create; > begin > WriteLn('Entering TDerived.Create'); > inherited Create; > WriteLn('Leaving TDerived.Create'); > end; > > constructor TBase.Create; > begin > WriteLn('Entering TBase.Create'); > Writeln('Leaving TBase.Create'); > end; > > begin > WriteLn('Creating a TBase and assigning to TBase variable...'); > c1 := TBase.Create; > WriteLn('Creating a TDerived and assigning to TBase variable...'); > c2 := TDerived.Create; > WriteLn('Creating a TDerived and assigning to TDerived variable...'); > c3 := TDerived.Create; > end. > > > The problem is, it makes NO DIFFERENCE at all in the following cases: > > CASE 1: > > TBase.Create; > TDerived.Create; > > CASE 2: > > TBase.Create; virtual; > TDerived.Create; virtual; > > CASE 3: > > TBase.Create; virtual; > TDerived.Create; override; > > According to the document, "inherited" cannot be used in non-virtual > methods, At least that's the result I would expect :) I can confirm your findings; contrary to the documentation, a simple test works correctly (the compiler prints no warnings or hints, both method and constructor work correctly, calling either a non-virtual from virtual, and virtual from non-virtual). > and it is wrong to use virtual in sub-class. It doesn't say that, it simply says that the redeclaration will not override the base implementation (the different behavior for 'object' is news to me!). FWIW your test doesn't actually "exercise" inheritance, since virtual constructors only make a difference when you call from a class reference (similarly, you'll only see a difference in virtual methods when calling from a base-typed variable). Code>>> var MyClass: TComponentClass = TDataModule; begin MyClass.Create(nil); <<< will instantiate a TDataModule. Code>>> var MyControl: TControl; begin MyControl := TButton.Create; MyControl.ExecuteDefaultAction; <<< will invoke TButton's implementation of ExecuteDefaultAction. > But my test shows the contrary. BTW, I am running Linux on 64bit platform. > > Regards, > Shannon Best regards, Flávio _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Reply by email

Martin
to Xiangrong Fang: I am reading this document: http://www.freepascal.org/docs-html/ref/refsu29.html and…
Xiangrong Fang wrote 17 lines
Hi All, I am reading this document: http://www.freepascal.org/docs-html/ref/refsu29.html and doing an experiment with the following code: program project1; {$mode objfpc}{$H+} type TBase = class constructor Create; virtual; end; TDerived = class(TBase) constructor Create; override; end;

....

Show quoted text 22 lines
The problem is, it makes NO DIFFERENCE at all in the following cases: CASE 1: TBase.Create; TDerived.Create; CASE 2: TBase.Create; virtual; TDerived.Create; virtual; CASE 3: TBase.Create; virtual; TDerived.Create; override; According to the document, "inherited" cannot be used in non-virtual methods, and it is wrong to use virtual in sub-class. But my test shows the contrary. BTW, I am running Linux on 64bit platform.

Using virtual with constructor makes a difference, if you use "class of" types

type
   TBaseClass = class of TBase;
   TDerivedClass = class of TDerived;
Var
   bc: TBaseClass;
begin
   bc:= TBase;
   bc.create;
   bc:= TDerived;
   bc.create;  // will call Tderived.create, but only in case 3
end;

Reply by email

Xiangrong Fang
to Martin

Thanks. Do you mean that the rules I see in the document apply to NORMAL virtual methods, but not virtual constructors?

Also, I have a related question: it seems that to override methods in ancestor it is required that the method has same signature, however reintroduce will not have such limitations. Example:

constructor TBase.Create; virtual;
... ...
constructor TDerived.Create(AParam: TParamType); override

The above code won't compile until I change the override keyword to "reintroduce". Because the compiler complains that there is no Create() with the same signature in the base class.

However, if I use reintroduce, according to the document, the compiler will make the Create method STATIC and hide the create method in the base class, in another word, it is then invalid to call TDerived.Create; (without param). What is the mechanism here?? Also, if I do not use override or reintroduce, instead, put an "overload" there, it will make BOTH version of the constructor visible, AND the "inherited" keyword also works well!

Could anyone explain these messes about: virtual, override, overload, reintroduce??

Thanks a lot.
Shannon

2013/8/20 Martin

Show quoted text 66 lines
On 20/08/2013 02:44, Xiangrong Fang wrote: Hi All, I am reading this document: http://www.freepascal.org/docs-html/ref/refsu29.html and doing an experiment with the following code: program project1; {$mode objfpc}{$H+} type TBase = class constructor Create; virtual; end; TDerived = class(TBase) constructor Create; override; end; .... The problem is, it makes NO DIFFERENCE at all in the following cases: CASE 1: TBase.Create; TDerived.Create; CASE 2: TBase.Create; virtual; TDerived.Create; virtual; CASE 3: TBase.Create; virtual; TDerived.Create; override; According to the document, "inherited" cannot be used in non-virtual methods, and it is wrong to use virtual in sub-class. But my test shows the contrary. BTW, I am running Linux on 64bit platform. Using virtual with constructor makes a difference, if you use "class of" types type TBaseClass = class of TBase; TDerivedClass = class of TDerived; Var bc: TBaseClass; begin bc:= TBase; bc.create; bc:= TDerived; bc.create; // will call Tderived.create, but only in case 3 end; _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Reply by email

Martin
to Xiangrong Fang
Xiangrong Fang wrote
Thanks. Do you mean that the rules I see in the document apply to NORMAL virtual methods, but not virtual constructors?

They apply to constructors too. But...

A virtual/overridden method is looked up based on the class used in code

TDerived.create; // the class given is TDerived
var
   b: TBase;
b := TDerived.create; // the class given still is TDerived

the assignment has nothing do to, what you class the method is called on.

b.Foo();  // Foo il looked up based on TBase

if b contains an instance of TDerived, then it depends on Foo being virtual.

Show quoted text 13 lines
Also, I have a related question: it seems that to override methods in ancestor it is required that the method has same signature, however reintroduce will not have such limitations. Example: constructor TBase.Create; virtual; ... ... constructor TDerived.Create(AParam: TParamType); override The above code won't compile until I change the override keyword to "reintroduce". Because the compiler complains that there is no Create() with the same signature in the base class.

Try to understand it on normal methods first. That is easier. (Technically it is the same for constructors, but you need a very abstract view, to realize this)

TBase = class

   procedure Foo; virtual;

...
TDerived = class

   procedure Foo; override;
var
   b: TBase;
d : TDerevied
WITHOUT virtual/overide
b := TDerived.Create

b.Foo; // will ca Foo declared on TBAse (because "b" is declared as TBase) d.Foo // always call Foo from TDerived, because d can NOT contain a TBase anyway

WITH virtual/overide
b := TDerived.Create
b.Foo; // will call Foo declared on TDerived (because "b" is CONTAINS a
TDerived)
ASSUMING (impossible) you wanted
   procedure Foo(a: String); override;

b.Foo; // should call Foo declared on either TFoo or TDerived, depending what was assigned to b
but if a TDerived was assigned, then it would break, because there is no argument.

so this is impossible

Show quoted text 7 lines
However, if I use reintroduce, according to the document, the compiler will make the Create method STATIC and hide the create method in the base class, in another word, it is then invalid to call TDerived.Create; (without param). What is the mechanism here?? Also, if I do not use override or reintroduce, instead, put an "overload" there, it will make BOTH version of the constructor visible, AND the "inherited" keyword also works well!

"reintroduce" is just a way to get rid of the warning, indicating that you did not accidental use the same name as in the base class

Reply by email

Antonio Fortuny
to Xiangrong Fang: Your findings confirmed mine, but not telling me why? It seems that the "virtual" keyword…
Xiangrong Fang wrote 17 lines
Hi Flavio, Your findings confirmed mine, but not telling me why? It seems that the "virtual" keyword has no use at all! To confirm this, I just removed the "inherited" call in TDerived, then re-run the program with or without "virtual/override", the result is exactly same, i.e. with c2 (declared as TBase), the following statements ALWAYS calls constructor of TDerived, NOT TBase: c2 := TDerived.Create; c2 := TBase(TDerived.Create); This is not same as the description in: http://www.freepascal.org/docs-html/ref/refsu26.html BTW, the above documents are talking about objects, but I am using classes, is there any difference here?

Yep: a class is a construct definig a collection of variables, properties and methods whilst an object is an instantiated class.

TMyClass = class(TSomething)            <<-------- this is a class
public
     constructor Create;
     blabla
end;

var
obj: TMyClass ; <<----------- this is a declared oblect which instantiates a class when created (see next line) ...

     Obj := TMyClass .Create;            <<----- now the class TMyClass

exists thru the Obj object

In other words, a class is non existent whilst an object falls into the real world. A class describes, an object makes use of a class A class is unique bet there can be lots of objects instantiating a class These few words help to explain the basic difference even if some more technical explations couls be needed. AFAIK, all OOP languages use almost the same terminology

This is why class procedures and functions exist. But this is another story.

Antonio.

Shannon
Sita Software *Antonio Fortuny* Senior Software engineer 220, avenue de la Liberté L-4602 Niederkorn Tel.: +352 58 00 93 - 93 www.sitasoftware.lu <http://www.sitasoftware.lu> Your IT Partner

Reply by email

Flávio Etrusco
to Xiangrong Fang: Your findings confirmed mine, but not telling me why? It seems that the "virtual" keyword…
Xiangrong Fang wrote
Hi Flavio, Your findings confirmed mine, but not telling me why?

I don't know why, maybe FPC used to be more strict about the use of inherited, or the docs are simply wrong.

It seems that the "virtual" keyword has no use at all!

It does, and the documentation is correct in this respect.

Show quoted text 8 lines
To confirm this, I just removed the "inherited" call in TDerived, then re-run the program with or without "virtual/override", the result is exactly same, i.e. with c2 (declared as TBase), the following statements ALWAYS calls constructor of TDerived, NOT TBase: c2 := TDerived.Create; c2 := TBase(TDerived.Create);

As I said - or tried to say ;) - in the other post, there won't be a difference when invoking the constructor on a "class literal" directly.
The second line will generate the exact same code as the first one; you're instantiating a TDerived instance then "upcasting" it to TBase.

This is not same as the description in: http://www.freepascal.org/docs-html/ref/refsu26.html

Note this page is about methods and you're using constructors in your example. I would recommend you first do your tests on method declarations, and later learn about class references (i.e. "class of" declarations) and constructors.

BTW, the above documents are talking about objects, but I am using classes, is there any difference here? Shannon

AFAIU per the first page you sent, you can't shadow/reintroduce a virtual method in an object; when you redeclare a virtual method it overrides the base one.

-Flávio

Reply by email

Sven Barth
to Antonio Fortuny: Yep: a class is a construct definig a collection of variables, properties and methods…
"Antonio Fortuny" wrote 7 lines
Le 20/08/2013 07:34, Xiangrong Fang a écrit : > > Hi Flavio, > > Your findings confirmed mine, but not telling me why? It seems that the

"virtual" keyword has no use at all! To confirm this, I just removed the "inherited" call in TDerived, then re-run the program with or without "virtual/override", the result is exactly same, i.e. with c2 (declared as TBase), the following statements ALWAYS calls constructor of TDerived, NOT TBase:

Show quoted text 5 lines
> > c2 := TDerived.Create; > c2 := TBase(TDerived.Create); > > This is not same as the description in:

http://www.freepascal.org/docs-html/ref/refsu26.html

> > BTW, the above documents are talking about objects, but I am using

classes, is there any difference here?

Yep: a class is a construct definig a collection of variables, properties

and methods whilst an object is an instantiated class.

You are aware that the link mentioned by Xiangrong Fang points to a part of the manual about Turbo Pascal style objects which are different in a different sense to Delphi style objects/classes?

Regards,
Sven

Reply by email

Antonio Fortuny
to Sven Barth
Sven Barth wrote 15 lines
>> >> This is not same as the description in: http://www.freepascal.org/docs-html/ref/refsu26.html >> >> BTW, the above documents are talking about objects, but I am using classes, is there any difference here? > > Yep: a class is a construct definig a collection of variables, properties and methods whilst an object is an instantiated class. You are aware that the link mentioned by Xiangrong Fang points to a part of the manual about Turbo Pascal style objects which are different in a different sense to Delphi style objects/classes?

Yes, but as he posts a question in a FPC list I imagine he is looking for some general information. He looks not to be in trouble. Lest's wait and see

Show quoted text 9 lines
Regards, Sven _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal
Sita Software *Antonio Fortuny* Senior Software engineer 220, avenue de la Liberté L-4602 Niederkorn Tel.: +352 58 00 93 - 93 www.sitasoftware.lu <http://www.sitasoftware.lu> Your IT Partner

Reply by email

Sven Barth
to Antonio Fortuny
"Antonio Fortuny" wrote 6 lines
Le 20/08/2013 14:34, Sven Barth a écrit : > > >> > >> This is not same as the description in:

http://www.freepascal.org/docs-html/ref/refsu26.html

> >> > >> BTW, the above documents are talking about objects, but I am using

classes, is there any difference here?

> > > > Yep: a class is a construct definig a collection of variables,

properties and methods whilst an object is an instantiated class.

> > You are aware that the link mentioned by Xiangrong Fang points to a part

of the manual about Turbo Pascal style objects which are different in a different sense to Delphi style objects/classes?

Yes, but as he posts a question in a FPC list I imagine he is looking for

some general information. He looks not to be in trouble. Lest's wait and see

That has nothing to do with FPC list or not. Xiangrong Fang asked about two different language features here and you told him something about a completely different difference related to one of the two language features. Your answer is simply not an answer to his question.

Regards,
Sven

Reply by email

Xiangrong Fang
to Sven Barth

Hi Sven,

Thank you for comment :) I am interested in a high level description of how these concepts (virtual / reintroduce / overloading) differs and how they affect programming, and I am currently not running into trouble, just try to get a better understanding of the language.

I'll explain my question / confusions:

1) I didn't notice that my question are related to "modes" of fpc, i.e. turbo-pascal/delphi/objfpc. If they are different, I would like to know more details, and I am more interested in objfpc mode, not those "compatibility" modes.

2) I know override / overload are completely different thing, I am not trying to compare apple with orange. Also, I am clear about the definition of virtual methods and their purpose. What I need help is to explain the observations I found with my demo app (which is a bit different than I expected). For example, why we need "reintroduce" while "overload" does the same job and better (it makes both methods available)?

Thanks,
Shannon

2013/8/20 Sven Barth

Show quoted text 36 lines
Am 20.08.2013 14:39 schrieb "Antonio Fortuny" : > > > Le 20/08/2013 14:34, Sven Barth a écrit : >> >> >> >> >> This is not same as the description in: http://www.freepascal.org/docs-html/ref/refsu26.html >> >> >> >> BTW, the above documents are talking about objects, but I am using classes, is there any difference here? >> > >> > Yep: a class is a construct definig a collection of variables, properties and methods whilst an object is an instantiated class. >> >> You are aware that the link mentioned by Xiangrong Fang points to a part of the manual about Turbo Pascal style objects which are different in a different sense to Delphi style objects/classes? > > Yes, but as he posts a question in a FPC list I imagine he is looking for some general information. He looks not to be in trouble. Lest's wait and see That has nothing to do with FPC list or not. Xiangrong Fang asked about two different language features here and you told him something about a completely different difference related to one of the two language features. Your answer is simply not an answer to his question. Regards, Sven _______________________________________________ fpc-pascal maillist - http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Reply by email

Sven Barth
to Xiangrong Fang
Xiangrong Fang wrote
1) I didn't notice that my question are related to "modes" of fpc, i.e. turbo-pascal/delphi/objfpc. If they are different, I would like to know more details, and I am more interested in objfpc mode, not those "compatibility" modes.

It has nothing to do with the modes besides the fact that the language construct "object" is available in all modes except macpas and that the language construct "class" is available in modes ObjFPC, Delphi, DelphiUnicode, MacPas (there also available as "object" for compatibility) and through the modeswitch "class". But objects and classes behave consistently to matter which mode is used.

Show quoted text 7 lines
2) I know override / overload are completely different thing, I am not trying to compare apple with orange. Also, I am clear about the definition of virtual methods and their purpose. What I need help is to explain the observations I found with my demo app (which is a bit different than I expected). For example, why we need "reintroduce" while "overload" does the same job and better (it makes both methods available)?

"reintroduce" allows you to "break" the chain of virtual methods to add a new parameter and to hide the old method (for example, because calling the inherited method in your child class breaks things or whatever). "overload" just allows you to define two (or more) methods with the same name that have different parameter lists (result type of functions does not matter!). In mode ObjFPC this is normally done automatically while in mode Delphi you need to explicitely declare them as "overload".

I hope this cleared things up a bit.

Regards,
Sven

Reply by email