Re: OO problem: How to create a duplicate of a derived class with only a base variable ?
From: Skybuck Flying (nospam_at_hotmail.com)
Date: 11/19/04
- Next message: Skybuck Flying: "Re: OO problem: How to create a duplicate of a derived class with only a base variable ?"
- Previous message: Skybuck Flying: "Re: OO problem: How to create a duplicate of a derived class with only a base variable ?"
- Maybe in reply to: Skybuck Flying: "OO problem: How to create a duplicate of a derived class with only a base variable ?"
- Next in thread: Skybuck Flying: "Re: OO problem: How to create a duplicate of a derived class with only a base variable ?"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Date: Fri, 19 Nov 2004 03:55:14 +0100
"Skybuck Flying" <nospam@hotmail.com> wrote in message news:...
>
> "Skybuck Flying" <nospam@hotmail.com> wrote in message news:...
> >
> > "Maarten Wiltink" <maarten@kittensandcats.net> wrote in message
> > news:419c71bb$2$21106$e4fe514c@news.xs4all.nl...
> > > "Skybuck Flying" <nospam@hotmail.com> wrote in message
> > > news:cnhf2r$s8i$1@news5.zwoll1.ov.home.nl...
> > >
> > > [...]
> > > > There is this base class. There are a few derived classess. There
> > > > is also a container class which is also derived from the base class.
> > > > The container class should be able to contain all derived classess
> > > > and classess of itself if necessary. The problem is with the indexed
> > > > property. The user should be able to "put" anything into the
> > > > container... the the property index type is Tbase. However... the
> > > > set procedure for the property should "create" a duplicate of this
> > > > element.
> > >
> > > Creating duplicates is almost never a good idea. Adding an object to a
> > > container should normally add the object to the container, not a copy
> >
> > I might have to agree with you on that.
> >
> > At first it seemed handy but now I might have memory leaks.
> > TBaseElement = class;
> > TBaseString = class(TBaseElement);
> > TBaseInteger = class(TBaseElement);
> >
> > var
> > BaseElement : TBaseElement;
> >
> > // create list
> > BaseElement := TBaseList.Create;
> >
> > // add some elements
> > TBaseList(BaseElement).Element[0] := TBaseString.Create;
> > TBaseList(BaseElement).Element[1] := TBaseString.Create;
> > TBaseList(BaseElement).Element[2] := TBaseInteger.Create;
> >
> > procedure TBaseList.SetElement(ElementIndex: integer; Element:
> > TBaseElement);
> > begin
> > mEncodedElement[ElementIndex] := Element.Duplicate( Element );
> > end;
> >
> > So... I think what happens is the following.
> >
> >
> > TBaseList(BaseElement).Element[0] := TBaseString.Create; // BaseString
> > created
> >
> > // BaseString passed to SetElement...
> >
> > // BaseString "re-created" and copied"
> >
> > // Now the question is what happens with the original BaseString which I
> > created manually... in this line:
> > // TBaseList(BaseElement).Element[0] := TBaseString.Create;
> >
> > In Java it would simply be garbage collected.
> >
> > *sigh* But will delphi clean it up automatically ???
> >
> > Probably not.. since Delphi says classess are nothing but pointers.
> >
> > The confusing part however is that Delphi also takes about "class
> > references" and "reference counted strings"
> >
> > So I can see how some programmer might start thinking that "class
> > references" are "reference counted" as well.
> >
> > I find it intuitive to start thinking like that... but nooooo Delphi
does
> > not do that. Classess are not reference counted last time I checked...
> that
> > means no auto-clean up.... the kinda weird thing is that a dynamic array
> of
> > class reference *is* auto cleaned up and all classess/objects in it as
> well
> > !
> >
> > Anyway that's a whole other subject about delphi's kinda inconsistency
:)
> >
> > Back to the topic...
> >
> > From the above it seems I don't have to make a copy at all. UNLESS ?! I
> want
> > to do this:
> >
> > var
> > KeepBaseStringAlive : TBaseString;
> >
> >
> > KeepBaseStringAlive := TBaseString.Create;
> >
> > TBaseList(BaseElement).Element[0] := KeepBaseStringAlive;
> >
> > KeepBaseStringAlive.Destroy;
> >
> > Do you see the problem "developing" here ?!
> >
> > If the object was not copied ?! but just the reference of it taken and
> > stored...
> >
> > *Exactly* ! then the object would no longer exist in the TbaseList !!!!
> >
> > I have to make sure that all the objects in the List stay alive no
matter
> > what... !
> >
> > Ok maybe that's not really a design requirement but still.
> >
> > Besides from that suppose I do use references instead and no copies...
> >
> > Then I am just writing create statements everywhere and no destroy
> > statements and let the list clean it up..
> >
> > That's funny enough exactly what I am doing now... and it's creating
> memory
> > leaks because I am copieing stuff hehe.
> >
> > This copieing is therefore unneccessary.
> >
> > *Sigh*... so I guess it's up to the user of the list/container to make
> sure
> > that the objects that are put into it stay alive... and trust the list
to
> > clean up instead ;)
> >
> > The container is actually a two way thing.. like coder/decoder... so the
> > container can create it's own objects/elements as well... but that won't
> be
> > a problem.
> >
> > I will change it to "references" instead and hope that I don't ran into
> > design problems later :)
> >
> > Anyway... I can see how it might lead to "design" problems...
> >
> > Since now I would loose the "copy" functionality of the property.
> >
> > So if I would need to duplicate objects in the future for some reason I
> > might have a problem there and then... and still need the duplicate
> > functions...
> >
> > Then I might end up writing something like:
> >
> > TBaseList(BaseElement).Element[0] := KeepBaseStringAlive.Duplicate; //
> then
> > it would return a duplicate of itself ;)
> >
> > That's not so bad :)
> >
> > > of it. But let's solve the problem at hand first, because it _is_ a
> > > somewhat interesting and non-trivial problem.
> > >
> > > Make TBase.Create virtual. This means its prototype is fixed for all
> > > descendant classes; this is the price you pay.
> >
> > Hmm.. I don't understand this yet... why does it have to be virtual ?
> >
> > Isn't the "deepest" child's constructor always called ?
> >
> > >
> > > Implement Assign and/or AssignTo in all descendant classes. Think
> > > carefully what makes sense for each combination of two classes. You
> > > can take advantage of polymorphism and reuse Assign and AssignTo
> > > functionality in various cases. Be sure to call inherited everywhere
> > > and add only what the current class adds. Implementing AssignTo for
> > > all classes that make sense lets you do most things without having
> > > to go back and edit existing classes for every new class.
> > >
> > > type TBaseClass = class of TBase;
> > >
> > > class function TBase.CopyObject(const Obj: TBase): TBase;
> > > begin
> > > Result:=((Obj.ClassType) as TBaseClass).Create(...);
> > > Result.Assign(Obj);
> > > end;
> >
> > Well I read delphi's help (again) and for the moment I think I see what
> the
> > use of (class of...) is for...
> >
> > "
> > A class-reference type, sometimes called a metaclass, is denoted by a
> > construction of the form
> >
> > class of type
> >
> > where type is any class type. The identifier type itself denotes a value
> > whose type is class of type. If type1 is an ancestor of type2, then
class
> of
> > type2 is assignment-compatible with class of type1. Thus
> >
> > type TClass = class of TObject;
> > var AnyObj: TClass;
> >
> > declares a variable called AnyObj that can hold a reference to any
class.
> > (The definition of a class-reference type cannot occur directly in a
> > variable declaration or parameter list.) You can assign the value nil to
a
> > variable of any class-reference type.
> >
> > To see how class-reference types are used, look at the declaration of
the
> > constructor for TCollection (in the Classes unit):
> >
> > type TCollectionItemClass = class of TCollectionItem;
> > ...
> > constructor Create(ItemClass: TCollectionItemClass);
> >
> > This declaration says that to create a TCollection instance object, you
> must
> > pass to the constructor the name of a class descending from
> TCollectionItem.
> >
> > Class-reference types are useful when you want to invoke a class method
or
> > virtual constructor on a class or object whose actual type is unknown at
> > compile time.
> >
> > Constructors and class references
> > "
> >
> > It's a way of passing "types" to parameters etc...
> >
> > And then later one can simply do... Parameter.Create... and it
> automatically
> > knows what type to create...
> >
> > Maybe this is usefull:
> >
> > "
> > Returns the class reference for the object's class.
> >
> >
> > Delphi syntax:
> >
> > function ClassType: TClass;
> >
> > C++ syntax:
> >
> > TClass __fastcall ClassType();
> >
> > Description
> >
> > ClassType dynamically determines the type of an object and returns its
> class
> > reference, or metaclass.
> >
> > Avoid using ClassType in application code.
> >
> > In Delphi code, use the is or as operators instead of ClassType.
> > In C++ code, use a dynamic cast or the InheritsFrom method instead of
> > ClassType.
> > "
> >
> > So it seems delphi classess already have a ClassType function ;)
> >
> > For a second I thought this was the solution but nope... this returns
> simply
> > the variable's type... and not the derived type etc.
> >
> > Though the "is" operator does match the derived type... (that's how I
> solved
> > the problem currently)
> >
> > Delphi help says: "is" operators on the dynamic types at runtime...
> > apperently the '=' operator does not... hehe.
> >
> > procedure TderivedContainer.DuplicateElement( ElementClass : TBaseClass;
> I:
> > integer );
> > begin
> > mElement[I] := ElementClass.Create;
> > end;
> >
> > var
> > // Something : TBaseClass; // cool, no typecasts needs to assign stuff
to
> > this "class/type reference variable" :)
> > // woops not so ;)
> >
> > Something : TBase;
> > begin
> >
> > Something := TderivedContainer.Create;
> >
> > TderivedContainer(Something).DuplicateElement( Tderived1, 0 );
> >
> > if TderivedContainer(Something).mElement[0] is Tderived1 then
> > writeln('yup');
> >
> >
> > if Something.ClassType = Tderived1 then
> > writeln('yup'); // does not display yup ?! kinda weird hehehe...
> >
> > readln;
> >
> >
> >
> >
> >
> > end.
> >
> > >
> > > You can add a new ClassType class method to TBase that returns a
> > > TBaseClass instead of the basic TClass, which gets rid of the ugly
> > > cast in the above code - by putting it somewhere else, of course.
> > > Nothing is free.
> > >
> > > class function TBase.ClassType: TBaseClass;
> > > begin
> > > Result:=(inherited ClassType) as TBase(Class?);
> > > end;
> >
> > Euhm... this example incomplete and weird... :)
> >
> > Why would you want the inherited classtype...
> >
> > I want the exact opposite ;) I want the derived ClassType :)
> >
> > >
> > > The hard part is definitely writing all the Assign[To] methods. Once
> > > you have the virtual constructor, most of the rest should fall into
> > > place.
> >
> > Well Assign is simply a member wise copy... that doesn't seem to hard.
> >
> > Sigh...
> >
> > Well both designs have their ups and downs.
> >
> > the "copy" solution lead to a memory leak (partially because delphi does
> not
> > auto clean up out of scope objects)
> > and is a lot slower probably. (maybe that's why java is a lot slower
:)
> I
> > am not sure if java has a copy operator or something or if that's common
I
> > do know java has references as well)
> >
> > the "referenced" solutions would at first not lead to memory leak.. and
> > would allow the user to lazy construct objects right into the property
> > etc...
> > it would also be faster.
> >
> > and in case "copy" functionality is needed each class would have it's
own
> > "duplicate" method ;)
> >
> > So then the second solution has the best of both worlds :)
> >
> > "reference" functionality at first for speed etc.
> >
> > "copy" functionality in case it is really needed =D
> >
> > Well I hope it all works out well.
> >
> > The problem that remains is figuring out if their is a way to learn the
> > "derived type" that is inside a "base type variable"
> >
> > The "is" operator will return the "deepest" child... (= derived type)
> >
> > For example
> >
> > var
> > A : TBase;
> >
> > begin
> >
> > A := TDerived;
> >
> > if A is TDerived then writeln( "true" ); // this will/should return
> > true.
> >
> > end;
> >
> > The drawback of the "is" operator is that it is needed for to check
> against
> > every possibly derived type.
> >
> > For example:
> >
> > // make a copy of a.
> > if A is TDerived1 then B := TDerived1.Create;
> > if A is TDerived2 then B := TDerived2.Create;
> > if A is TDerived3 then B := TDerived3.Create;
> >
> > // etc...
> >
> > Ok this problem is easily solved with
> >
> > B := A.duplicate;
> >
> > But that's because the method duplicate knows what type it belongs
too...
> >
> > The problems start when an "external" object needs to store these mixed
> > derrived types and needs to duplicate them...
> >
> > Container.SomeMethod( A : TBase );
> > begin
> > // duplicate A
> >
> > B := A.duplicate; // can't call duplicate because ancoster does
> not
> > have it ? <- no true true...
> > // ok even this can be fixed with an virtual/abstract method in
> the
> > base class.
> >
> >
> > end;
> >
> > Well the problem is what the class is supposed to do the container...
> >
> > It needs to construct something.
> >
> > And I guess there is a little problem since all those methods were a
litte
> > bit different..
> >
> > If they all were the same... which would be possible then there is no
more
> > problem.
> >
> > So at the moment I see no remaining problems... and I see no need to use
> > class of types etc...
> >
> > But that was not my question hehehe.
> >
> > My original question was if all these duplicate functions are needed...
> >
> > I think the simply answer to that is simply "yes"
> >
> > Since delphi does not have any "duplicate" functions...
> >
> > It only has a "create" functionality... and that's not quite the same.
> >
> > It does have a "assign" functionality... but that's also not quite the
> same.
> > ( well not really you have to make it yourself :) )
> >
> > So combining these would give a duplicate function.
> >
> > So only one question remains:
> >
> > Is it enough to write a "base" duplication function.
> >
> > The answer is probably "NO !"
> >
> > Because the base class does not know about the properties/fields of the
> > "derived classess".
> >
> > In short: The base classess simply do not know how to "duplicate" the
> > derived classess.
> >
> > Only the derived classess now that.
> >
> > So that means the derived classess "have" to have a assign and
optionality
> a
> > duplicate function for easy use.
> >
> > So the answer is half answered:
> >
> > 1. Yes a base class can make a duplicate of the derived class... by
simply
> > calling it's abstract duplicate function... at least that's what I
> > believe... I might be wrong...
> >
> > Well actually I dont really believe that... that would be weird.. let's
> find
> > out :)
> >
> > Wow... I just tested it... and the weird thing is... it is possible for
a
> > base class to call the "derived" versions if it really is a derived type
> >
> > by overrriding the methods
> >
> > this example illustrates that:
> >
> > program Project1;
> >
> > {$APPTYPE CONSOLE}
> >
> > uses
> > SysUtils;
> >
> > type
> > Tbase = class
> > private
> >
> > public
> >
> > procedure Test; virtual;
> >
> > end;
> >
> > Tderived = class(Tbase)
> > private
> >
> > public
> >
> > // procedure Test; // Base.Test will be called by Base.Test
> > // procedure Test; virtual; // Base.Test will still be called by
> Base.Test
> >
> > procedure Test; override; // Derived.Test will be called by Base.Test
> !!!
> >
> > end;
> >
> > procedure Tbase.Test;
> > begin
> > writeln('base test');
> > end;
> >
> > procedure Tderived.Test;
> > begin
> > writeln('derived test');
> >
> > end;
> >
> > var
> > b : Tbase;
> >
> > begin
> >
> > b := Tderived.Create;
> > b.Test;
> > b.Destroy;
> >
> > readln;
> >
> > end.
> >
> > This could be usefull if all derived versions stay the same and
> overrided...
> > then that was safe some typecasting and "is" operators etc... for for
> > example the container :)
> >
> > But then the design is less free... it has to be the same...
> >
> > Well for now I don't like less free design... so as long as the number
of
> > different types remains low... using the "is" operator is doable :)
> >
> > 2. But for the base class to be able to duplicate... the derived classes
> > must have a duplicate method...
> >
> > Lol... what an abstract post hehe.
> >
> > In short the bottum line is:
> >
> > base type variables can call override derived type methods when the base
> > type variable contains a derived type and the method "signature" is the
> same
> > as the base type. This restricts the design freedom but is usefull when
> > there are many derived types and one has to for example code a container
> > which has to operate on all derived types and has to call their
methods...
> > thanks to these overrided "locked in/fixed" derived method types the
> > container can simply work with the base type and there is no need to use
> the
> > "is" operator or typecastings etc.
> >
> > An example of this is the Assign methods and my Duplicate methods ;) all
> > derived classess should have them ;)
> >
> > This is all achieved without "class of class type" types...
> >
> > I am sure those kind of types might have their uses but I dont see where
> it
> > could be usefull in this case :) since in this case everything can be
done
> > with the constructors, assign and duplicate methods.
> >
> > So using these "class of class type" types might just be an alternative
> > implementation without any benefits ? since I would estimate the same
> > ammount of code is needed. (For example... every derived type would need
a
> > "Type function" to return it's "ClassType" )
> >
> > So far the only example I can imagine is ClassType.Create;
> >
> > Because calling any other function might be weird because how would you
> know
> > if ClassType.Test existed ??? <- wierd... maybe this is all figured out
at
> > compile time... ?
> >
> > Yeah maybe that's the difference... my way might work at runtime too
:)...
> > maybe the class type things work only at design time ?
>
> Well I was pretty right about it as just being an alternative
> implementation... it turns out "Class Of" types only have a limited number
> of methods...
>
> This example illustrates it ;)
>
> Calling
>
> Something.MyClassType.Derived2Only won't compile ;)
>
> So I guess it would still be needed to use other solutions... like the
"is"
> operator... or using "fixed/override methods" ;)
>
> (
> well design/run time i dont know... all I know now is that "class of"
types
> are very limited... the have a very limited number of methods :)
>
> Hmmm I wonder if they can be type casted ? :):):):)
> )
>
> program Project1;
>
> {$APPTYPE CONSOLE}
>
> uses
> SysUtils;
>
> type
> TbaseClass = class of Tbase;
>
> Tbase = class
> private
>
> public
>
> function MyClassType : TbaseClass; virtual;
>
> end;
>
> Tderived1 = class(Tbase)
> private
> public
> a,b,c : string;
>
> function MyClassType : TbaseClass; override;
>
> end;
>
> Tderived2 = class(Tbase)
> private
> public
> i : integer;
>
> function MyClassType : TbaseClass; override;
>
> function Derived2Only;
should have been
procedure Derived2Only;
;)
> end;
>
> function Tbase.MyClassType : TbaseClass;
> begin
> result := Tbase;
> end;
>
> function Tderived1.MyClassType : TbaseClass;
> begin
> result := Tderived1;
> end;
>
> function Tderived2.MyClassType : TbaseClass;
> begin
> result := Tderived2;
> end;
>
> procedure Tderived2.Derived2Only;
> begin
> writeln('This method only exists in Derived 2');
> end;
>
> var
> Something : Tbase;
>
> begin
>
> Something := Tderived1.Create;
>
> if Something.MyClassType = Tderived1 then
> begin
> writeln('yup');
>
> // what if I call a non existent method like Derived2Only.
>
> // oh I see... Class of Types only have a limited number of methods
> // so it can't be called here... not like this ;)
>
> // only the limited methods like the constructor can be called here
> // destructor exists as well..
> // these methods are actually part of Tobject :) ;)
> Something.MyClassType.
This is kinda funky: =D
// nope can't be used to typecast ? hehe
// Something.MyClassType( Something );
// wow this is wierd ?!
// something is actually a Tderived1
// but with this trick the method of Tderived2 can still be called ?
// wow is this valid/legal/robust ? or could it lead to access violations
?
// I guess as long as Derived2Only does not attempt to access any fields
// it would possible work... or maybe if it is the same size
// it could actually still work with the data of a derived 1 instance ?!
// wow... that's like "method type" casting on object data :)
TDerived2( Something.MyClassType ).Derived2Only;
> end;
>
> readln;
>
> end.
>
> Bye,
> Skybuck.
>
>
- Next message: Skybuck Flying: "Re: OO problem: How to create a duplicate of a derived class with only a base variable ?"
- Previous message: Skybuck Flying: "Re: OO problem: How to create a duplicate of a derived class with only a base variable ?"
- Maybe in reply to: Skybuck Flying: "OO problem: How to create a duplicate of a derived class with only a base variable ?"
- Next in thread: Skybuck Flying: "Re: OO problem: How to create a duplicate of a derived class with only a base variable ?"
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Relevant Pages
|