Jump to content
SgtZdog

Static Method in Child Class Access Protected Parent Member

Recommended Posts

Hey all, I'm a rather new to Delphi (a few years programming in C#, C++, Python, and Clojure).

(A note, the original thought that brought up this question is abandoned as I thought of a somewhat better solution for when I was originally writing this question, but the core question still seems good to have an answer for,)
I'm trying to have a static method in a child class access protected members declared in the parent, but having trouble figuring out how to do it. Is it not possible or am I doing it wrong? Simplified code example:

 

edit: I tried making the method I want to use owned by the child class instead (parent class doesn't particularly need to own it anyway), but that doesn't seem to work either.

 

unit1.pas:

interface
	type
		TParentClass = class
			protected
				function ProtectedMember(); Boolean; virtual; abstract;

unit2.pas

interface
	type
		ChildClass = class(ParentClass)
        	public
            	//Question 1)
            	class function Maker() : ChildClass;
                //Question 2) (I haven't tested this, so maybe this works no problem?)
                class procedure Manipulator(instance : ParentClass);
                //Question 3) (I haven't tested this, so maybe this works no problem?)
                procedure Stuffer(instance2 : ParentClass);
            protected
            	procedure ChildOwnedMethod();
         end;
         
implementation
	class function ChildClass.Maker() : ChildClass;
    begin
    	Result := ChildClass.Create(); //Edit: No longer incorrectly use inherited
        Result.ProtectedMember(); //Trying to do this and it doesn't seem to work
        Result.ChildOwnedMethod(); //Bonus! I tried doing this instead and it doesn't seem to work either.
    end;
    
    class procedure ChildClass.Manipulator(instance : ParentClass);
    begin
    	instance.ProtectedMember(); //Does this work? Is there a way to do it?
    end;
    
    procedure ChildClass.Stuffer(instance2 : ParentClass);
    begin
    	ProtectedMember(); //I would expect this to work
        instance2.ProtectedMember(); //Does this work though? Is there a way to do it?
    end;
    
    procedure ChildClass.ChildOwnedMethod();
    begin
    	//Do Stuff
    end;

 

Edited by SgtZdog
additional question to add

Share this post


Link to post

@SgtZdog

  • ParentClass: it's a class like any other! no problem!

    • BUT, it has a "virtual/abstract" method!!! --- so this says that the "subclasses" must "mandatory" implement it, otherwise you'll have an access violation when in use!!! --- but fortunately, (should) an exception will be created during the usage of "ChildClass", showing the "abstract method" never implemented in the subclasses!!!

  • In general, I think it's not right for your classes, because "ChildClass" intends to use a method defined in "ParentClass", but "ParentClass" doesn't implement that method and requires it to be implemented by subclasses... I think we have a " impasse" here no?
  • Now, if the "subclasses" implement this method, "it" does not belong to the "ParentClass", but to the "ChildClasses"!!!
  • Another problem to take into account is mixing "common method" with "class method". Remembering that the "class methods" belong to the class, and will be accessible to any number of instances of objects created from the "Childclass". That is, they can be read, written, etc... by any objects of this "ChildClass" hierarchy.
  • Another factor, too, is the accessibility between "common methods" and "class methods", that is, the conversation between them, internally!
unit uParentClass;

interface

type
  TParentClass = class
  protected
    // a "common method"
    function MethodWhatHappensHere: string; virtual; abstract; // it will be implemented on subclasses!
  end;
implementation

end.

// ------------ unit uChildClass -----------------
interface

uses
  uParentClass;

type
  TChilClassUsingInheriting = class(TParentClass)
  protected
   // it's mandatory implement it here! -- uncomment it!!!
    // function MethodWhatHappensHere: string; override; // implementing... --- E2362 Cannot access protected symbol "xxxxxxxxx" directly
  public
    function ShowSomeMessage: string; // now, it's possible!
  end;

implementation

{ TChilClassUsingInheriting }

function TChilClassUsingInheriting.ShowSomeMessage: string;
begin
  // if "MethodWhatHappensHere" was not defined in this subclass, then the "ancestral" will be used anyway!
  //
  result := {inherited}  MethodWhatHappensHere;  // using "inherited" you call the "ancestral" method, BUT remember: there, it is "abstract"!!!
  //result := { Self. }  MethodWhatHappensHere;  // using "Self." it's ok if was defined on "ChildClass"!!! 
end;

// function TChilClassUsingInheriting.MethodWhatHappensHere: string;
// begin
// result := 'I dont know! from: ' + Self.ClassName;
// end;

end.

// -------- form1 ---------
implementation

{$R *.dfm}

uses
  uChildClass;

procedure TForm1.Button1Click(Sender: TObject);
var
  LChildClass: TChilClassUsingInheriting;
begin
 // Unit1.pas(42): W1020 Constructing instance of 'TChilClassUsingInheriting' containing abstract method 'TParentClass.MethodWhatHappensHere'
  LChildClass := TChilClassUsingInheriting.Create;
  try
    LChildClass.ShowSomeMessage;
  finally
    LChildClass.Free;
  end;
end;

 

Edited by programmerdelphi2k

Share this post


Link to post
class function ChildClass.Maker() : ChildClass;
begin
  Result := inherited Create(); // calling "ancestral Create" as Child result? ... some sense here?
...
end;
  1. if "Child" inherit from "Parent", why call "Create from Parent as a result as Child"?
  2. if  "Maker" is a class method, then why create a instance as Child, if class method does not needs any creation?
  3. if was ok, remember: "Parent" has a "abstract method" that needs to be implmented on "Child"

confused not?

 

Share this post


Link to post

Ah, this was a good catch: 

Quote

// calling "ancestral Create" as Child result? ... some sense here?

Yeah, that was not intended and was my mistake. I fixed up the original post to do things correctly. Things seem to be working now, thank you very much for your help.

 

To hopefully answer the core of your questions, the intent of the protected abstract method is to enforce child classes having to have it so that a parent method can call it and lean on a correct implementation being available courtesy polymorphism. As I mentioned I've changed tack, so this isn't what I'm going to do, but this was still good practice at Delphi syntax for me.

Share this post


Link to post

static (ie, 'class' methods) cannot access members since they don't refer to any instance of an object.

 

You seem to be conflating a Factory pattern with ... a mess (?) 

 

Are you wanting a Singleton here? 

 

i can't really tell what you're trying to accomplish. 

 

Try explaining it in words first because your code makes no sense to me.

 

 

  • Like 1

Share this post


Link to post

@SgtZdog 

unit uParentClass;

interface

type
  TParentClass = class
  protected
    // now, this class can to be used as any other... directly or inherited!
    function PleaseOverrideMeIfWant: string; virtual; // no needs to be overrided on subclasses!
  end;

implementation

{ TParentClass }

function TParentClass.PleaseOverrideMeIfWant: string;
begin
  result := 'hello, im TParentClass definition';
end;

end.
-------------- child -----------------
unit uChildClass;

interface

uses
  uParentClass;

type
  TChildClassInheritingFromParent = class(TParentClass)
  private
    function GetPleaseOverrideMeIfWant: string;
  protected
    function PleaseOverrideMeIfWant: string; override; // overriding ancestral function...
  public
    property ReadMyProtectMethodOverrited  : string read PleaseOverrideMeIfWant;
    property ReadMyProtectMethodNoOverrited: string read GetPleaseOverrideMeIfWant;
  end;

implementation

{ TChildClassInheritingFromParent }

function TChildClassInheritingFromParent.GetPleaseOverrideMeIfWant: string;
begin
  result := inherited PleaseOverrideMeIfWant; // using ancestral...
end;

function TChildClassInheritingFromParent.PleaseOverrideMeIfWant: string;
begin
  result := 'hello, im TChilClass definition overriding TParentClass definition...';
end;

end.
------------------- form1 --------------
implementation

{$R *.dfm}

uses
  uChildClass;

procedure TForm1.Button1Click(Sender: TObject);
var
  LChilClass: TChildClassInheritingFromParent;
begin
  LChilClass := TChildClassInheritingFromParent.Create;
  try
    Memo1.Lines.Add(LChilClass.ReadMyProtectMethodOverrited);
    //
    Memo1.Lines.Add(LChilClass.ReadMyProtectMethodNoOverrited);
  finally
    LChilClass.Free;
  end;
end;

initialization

ReportMemoryLeaksOnShutdown := true;

end.

image.png.56d42d68d731d4d6ae94143b4f500f16.png

Edited by programmerdelphi2k

Share this post


Link to post
8 hours ago, David Schwartz said:

static (ie, 'class' methods) cannot access members since they don't refer to any instance of an object.

 

You seem to be conflating a Factory pattern with ... a mess (?) 

 

Are you wanting a Singleton here? 

 

i can't really tell what you're trying to accomplish. 

 

Try explaining it in words first because your code makes no sense to me.

 

 

I was indeed going for something like a factory pattern (though simplified in that each child class provides static methods for construction instead of having a separate class for the sole purpose of construction). Rest assured, as mentioned above, I'm reducing a bunch of this. Reason for the factory style idea though was to return nil instead of raising an exception in the case of bad construction. I might still keep the static construction methods, but they will at least avoid the unnecessary polymorphism.

 

The current task I'm working on is overhauling a logging system for a game (these are user facing reports). The messages have quite complicated construction and I'm trying to test the viability of removing the message generating from the game logic (this is beneficial in that it allows us to more easily and dynamically have actions like highlighting given units in the messages).

  • Like 1

Share this post


Link to post

Programmer, 

 

I definitely thought about virtual, but the current base class has no need for the virtual methods and under my current concept construction of the base class would be replaced by more specific child classes. (I'm assuming your use of properties was just for demonstration purposes and not part of a proposed solution.)

Share this post


Link to post

@SgtZdog  of course, my props was more to show, not to use. as I dont know exactly what you expect ...

 

as any log-system I think that a Singleton Pattern as "container" would can be used as your repository of messages, or a Observer Pattern... 

Share this post


Link to post

Yes, there is a separate class not described by my current efforts that holds the messages and deals with displaying them. This is for the messages themselves. So the entire log is held by one class, with each message being an instance of something that inherits from the parent class here. Our legacy system messages were plain strings and then some regex would let us add functionality. I'm experimenting with moving away from these plain strings for speed, ease of use, and capability purposes.

MessageClass

   /         \
type1   type2

Log:
  contains array of MessageClass

Edited by SgtZdog

Share this post


Link to post

The class is nothing terribly complex, but here's the interface with the core functions (for brevity I'm not including everything like I/O for saving and loading):

  TMessageLog = class
  public
    constructor Create();
    destructor  Destroy(); override;

    procedure   Clear();

    procedure AddEntry(aLogEntry : TMessageLogEntry);

    function  GetLogEntries(reporter : Integer) : TArray<TMessageLogEntry>; overload;
    function  GetLogEntries(reporter : Integer; anUntilTime : TDateTime) : TArray<TMessageLogEntry>; overload;
    function  GetLogEntriesMatchingAnyTag(reporter : Integer; const theTags : TArray<TMessageLogTag>) : TArray<TMessageLogEntry>; overload;
  end;

(I'll note, I am not the original author of this class and it is still subject to change)

Share this post


Link to post

how the message will be processed when received? said, who send? who read? the messages, what object is it? (string/object/etc...) is thread-safe or what?

 

Share this post


Link to post

Messages aren't really processed by the log, just held by it, barring the filtering provided by the different GetLogEntries methods. Pretty much anywhere in the application can read/write the log (note that we functionally only have a few places that read from it, but many that write to it). The messages are objects that I'm currently experimenting with using some inheritance to simplify things sort of since we have over 100 different possible message formats. The application is broadly speaking single-threaded and not thread safe.

 

edit: The log is a global right now, and fixing it up to be singleton is probably on my todo list.

Edited by SgtZdog

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×