Sign in Join

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

Mailing lists / fpc-pascal / 2014-02

Wrong constructor returning the right object

8 messages · 3 people ·

Reply by email Subscribe to this list Your subscription

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

Mark Morgan Lloyd

I think I've sorted this but I'd appreciate being kicked if it looks like I'm doing something badly wrong.

What determines the constructor that's called when an object's created?

I've got a couple of classes defined like this:

TB5500BaseUnit=
class(TObject)

       constructor Create(enum: integer; mnem: String7; unitIndex:

integer; unitDesignate: integer; cc: TObject);
..

TB5500SPOUnit=
class (TB5500BaseUnit)

       constructor Create(enum: integer; mnem: String7; unitIndex:

integer; unitDesignate: integer; cc: TObject);
..

The actual objects being created are in response to configuration tables and files:

var
unitClass: class of TB5500BaseUnit; (* Ancestor of all I/O devices *)

   u: TB5500BaseUnit;
   name: string;                       (* For debugging *)

..

         unitClass := LookupUnitType( { from device name } );
         name := unitClass.ClassName;    (* For debugging *)
         name := '';                     (* Make sure it's wiped *)
         u := unitClass.Create(Ord(mnem), specs.unitName,
specs.unitIndex, specs.designate, self);
         name := u.ClassName;            (* For debugging *)
         thisUnit[specs.unitIndex] := u;

If I was trying to create a TB5500SPOUnit I was finding that unitclass was set correctly, and that u was getting a valid type with (later) public methods declared as overrides being available. The odd thing was that TB5500SPOUnit.Create() was not being called, but instead TB5500BaseUnit.Create().

I find that if I explicitly decorate TB5500BaseUnit.Create() as virtual and TB5500SPOUnit.Create() as override then TB5500SPOUnit.Create() is called correctly. I didn't think this was necessary, but on reflection I assume that it's because u is declared as a TB5500BaseUnit: the variable's compile-time rather than run-time class is being used for the constructor unless explicitly overridden.

Is there an obvious way of tiying this up that I'm overlooking?

Mark Morgan Lloyd markMLl .AT. telemetry.co .DOT. uk [Opinions above are the author's, not those of his employers or colleagues]

Reply by email

Jeppe Græsdal Johansen
to Mark Morgan Lloyd
Mark Morgan Lloyd wrote 51 lines
I think I've sorted this but I'd appreciate being kicked if it looks like I'm doing something badly wrong. What determines the constructor that's called when an object's created? I've got a couple of classes defined like this: TB5500BaseUnit= class(TObject) constructor Create(enum: integer; mnem: String7; unitIndex: integer; unitDesignate: integer; cc: TObject); .. TB5500SPOUnit= class (TB5500BaseUnit) constructor Create(enum: integer; mnem: String7; unitIndex: integer; unitDesignate: integer; cc: TObject); .. The actual objects being created are in response to configuration tables and files: var unitClass: class of TB5500BaseUnit; (* Ancestor of all I/O devices *) u: TB5500BaseUnit; name: string; (* For debugging *) .. unitClass := LookupUnitType( { from device name } ); name := unitClass.ClassName; (* For debugging *) name := ''; (* Make sure it's wiped *) u := unitClass.Create(Ord(mnem), specs.unitName, specs.unitIndex, specs.designate, self); name := u.ClassName; (* For debugging *) thisUnit[specs.unitIndex] := u; If I was trying to create a TB5500SPOUnit I was finding that unitclass was set correctly, and that u was getting a valid type with (later) public methods declared as overrides being available. The odd thing was that TB5500SPOUnit.Create() was not being called, but instead TB5500BaseUnit.Create(). I find that if I explicitly decorate TB5500BaseUnit.Create() as virtual and TB5500SPOUnit.Create() as override then TB5500SPOUnit.Create() is called correctly. I didn't think this was necessary, but on reflection I assume that it's because u is declared as a TB5500BaseUnit: the variable's compile-time rather than run-time class is being used for the constructor unless explicitly overridden. Is there an obvious way of tiying this up that I'm overlooking?

You have forgotten to declare the constructor virtual.

Reply by email

Mark Morgan Lloyd
to Jeppe Græsdal Johansen
Jeppe Græsdal Johansen wrote 10 lines
> I find that if I explicitly decorate TB5500BaseUnit.Create() as > virtual and TB5500SPOUnit.Create() as override then > TB5500SPOUnit.Create() is called correctly. I didn't think this was > necessary, but on reflection I assume that it's because u is declared > as a TB5500BaseUnit: the variable's compile-time rather than run-time > class is being used for the constructor unless explicitly overridden. > > Is there an obvious way of tiying this up that I'm overlooking? > You have forgotten to declare the constructor virtual.

I said I'd fixed it by declaring the constructor virtual, but that I'm not entirely confident in my understanding of why that's necessary.

Mark Morgan Lloyd markMLl .AT. telemetry.co .DOT. uk [Opinions above are the author's, not those of his employers or colleagues]

Reply by email

Marco van de Voort
to Mark Morgan Lloyd: I think I've sorted this but I'd appreciate being kicked if it looks like I'm doing…

In our previous episode, Mark Morgan Lloyd said:

That logical, since the constructors are not an virtual; and override; pair.

I find that if I explicitly decorate TB5500BaseUnit.Create() as virtual and TB5500SPOUnit.Create() as override then TB5500SPOUnit.Create() is called correctly.

That's the normal way.

I didn't think this was necessary,

_why_ did you think this?

but on reflection I assume that it's because u is declared as a TB5500BaseUnit: the variable's compile-time rather than run-time class is being used for the constructor unless explicitly overridden.

In theory this kind of information could propagate in simple cases, but then behaviour would differ between cases where this information could be propagated (a deeper version called), and not (base version called).

Reply by email

Mark Morgan Lloyd
to Marco van de Voort
Marco van de Voort wrote 13 lines
In our previous episode, Mark Morgan Lloyd said: That logical, since the constructors are not an virtual; and override; pair. > I find that if I explicitly decorate TB5500BaseUnit.Create() as virtual > and TB5500SPOUnit.Create() as override then TB5500SPOUnit.Create() is > called correctly. That's the normal way. > I didn't think this was necessary, _why_ did you think this?

In part, because the documentation on method declarations at http://www.freepascal.org/docs-html/ref/refsu25.html#x66-730005.5.1 explicitly allows a virtual method directive, but the documentation on constructor declarations at
http://www.freepascal.org/docs-html/ref/refse28.html#x64-710005.4 explicitly does not allow it.

Show quoted text 8 lines
> but on reflection I > assume that it's because u is declared as a TB5500BaseUnit: the > variable's compile-time rather than run-time class is being used for the > constructor unless explicitly overridden. In theory this kind of information could propagate in simple cases, but then behaviour would differ between cases where this information could be propagated (a deeper version called), and not (base version called).
Mark Morgan Lloyd markMLl .AT. telemetry.co .DOT. uk [Opinions above are the author's, not those of his employers or colleagues]

Reply by email

Marco van de Voort
to Mark Morgan Lloyd

In our previous episode, Mark Morgan Lloyd said:

Show quoted text 8 lines
>> I didn't think this was necessary, > > _why_ did you think this? In part, because the documentation on method declarations at http://www.freepascal.org/docs-html/ref/refsu25.html#x66-730005.5.1 explicitly allows a virtual method directive, but the documentation on constructor declarations at

That link also contains a case for constructors, but point taken, the railroad diagram of constructor could be improved, which might be worthy of a bugreport.

Reply by email

Mark Morgan Lloyd
to Marco van de Voort
Marco van de Voort wrote 11 lines
In our previous episode, Mark Morgan Lloyd said: >>> I didn't think this was necessary, >> _why_ did you think this? > In part, because the documentation on method declarations at > http://www.freepascal.org/docs-html/ref/refsu25.html#x66-730005.5.1 > explicitly allows a virtual method directive, but the documentation on > constructor declarations at That link also contains a case for constructors, but point taken, the railroad diagram of constructor could be improved, which might be worthy of a bugreport.

"Railroad diagram"- I've not heard it called that since I worked for the Yanks :-) Bugreported as http://mantis.freepascal.org/view.php?id=25770

I also note the comment about classes with virtual methods /having/ to have an explicit constructor and destructor: does this apply to classes that have *override* methods or only to parent classes with one or more *virtual* methods?

Mark Morgan Lloyd markMLl .AT. telemetry.co .DOT. uk [Opinions above are the author's, not those of his employers or colleagues]

Reply by email

Jeppe Græsdal Johansen
to Mark Morgan Lloyd: In part, because the documentation on method declarations at…
Mark Morgan Lloyd wrote 22 lines
Marco van de Voort wrote: > In our previous episode, Mark Morgan Lloyd said: > > That logical, since the constructors are not an virtual; and > override; pair. > >> I find that if I explicitly decorate TB5500BaseUnit.Create() as >> virtual and TB5500SPOUnit.Create() as override then >> TB5500SPOUnit.Create() is called correctly. > > That's the normal way. > >> I didn't think this was necessary, > > _why_ did you think this? In part, because the documentation on method declarations at http://www.freepascal.org/docs-html/ref/refsu25.html#x66-730005.5.1 explicitly allows a virtual method directive, but the documentation on constructor declarations at http://www.freepascal.org/docs-html/ref/refse28.html#x64-710005.4 explicitly does not allow it.

That documentation is about objects, not classes.

Here's the relevant part for classes:
http://www.freepascal.org/docs-html/ref/refsu25.html#x66-730005.5.1

Reply by email