tags:

views:

238

answers:

5

I have a class of mine, lets call it TMyObject, which should return a slightly modified copy of itself. So, one of its functions should return an object of the same type as itself:

function TMyObject.TrimEnds: TMyObject;
begin
  Result:= TMyObject.Create;
  Result.DoStuff;
edn;

Can I do that? Is it legit what am I doing?

I mean, I already tried it and the compiler allows me to do it, but I wonder if there will be long time/hidden negative effects.

Any thoughts will be appreciated. Thanks.


Edit: The new slightly modified copy will be saved to disk. It is some kind of 'Save as...'. How it works: The original object creates a copy of itself, instructs this copy to do some changes and to save to disk. Then the original frees the copy. This way I keep the original object in memory unchanged but I have a modified version of this to disk.

You may think that my object holds a picture. What I need is a function that returns a slightly modified copy of the picture.

+1  A: 

there is absolutely nothing wrong with what you wrote. you are simply returning an object and this is perfectly valid, it could be any other object of any other type.

Adrien Plisson
+5  A: 

but I wonder if there will be long time/hidden negative effects.

I don't see any, and I used to do this with my own linked lists, and never had any problem. I think it is pretty much the same as creating an instance in any other place.

eKek0
Great. Many thanks to all.
Altar
Be sure you free up resources. Otherwise, your objects stays in memory and is never freed. Well, on program termination it is.
Scoregraphic
+2  A: 

I guess you are right, this piece of code seems wrong to me. You always have to make it clear who's responsible for freeing the object you return. In most cases, this object will not be freed.

A better approach is to usually let the caller create the object, pass it to your method (you only need a procedure then) which modifies it.

I am curious to know why you would want to return a "slightly modified version" of your object. It sounds counter-intuitive to me...

kuzkot
Please see my original post. I have added more explanations.
Altar
+2  A: 

As others had said, there's nothing wrong with that but there may be better ways.

Variant 1: Change this into class method and give it a meaningful name.

class function TMyObject.CreateSpecialized: TMyObject;
begin
  Result := TMyObject.Create;
  //initialize Result
end;

anObj := TMyObject.CreateSpecialized;

Variant 2: Use a constructor. You can have multiple constructors in a class.

constructor TMyObject.CreateSpecialized;
begin 
  Create; // make sure everything is initialized correctly
  // now do custom initialization
end;

anObj := TMyObject.CreateSpecialized;

Usage is same in both examples but in second case your intentions are clearer to a random reader.

If you want to take one object and create another one based on first object's fields, use a constructor with parameter.

constructor TMyObject.CreateSpecialized(obj: TMyObject);
begin
  Create;
  intField := obj.IntField * 2;
end;

anObj := TMyObject.CreateSpecialized(otherObj);
gabr
+1 for class method
RRUZ
There is some tradition on calling your last example a "copy constructor"
PA
@PA: No, as a copy constructor would create an identical *copy*, not a mutated one. Copy constructors aren't used much in Delphi. Since all objects are accessed solely via references there is seldom a need for them.
mghie
@mghie: Agree, but sometimes they are useful. In those cases I like to name them Clone.
gabr
+2  A: 

If you have a new class derived from TMyObject like TMyOtherObject = class(TMyObject), the TrimEnds function will still return a TMyObject instead of a TMyOtherObject as one might expect.

You can fix this by using this scheme:

TMyObjectClass = class of TMyObject;

function TMyObject.TrimEnds: TMyObject;
begin
  Result:= TMyObjectClass(ClassType).Create;
  Result.DoStuff;
end;
Uwe Raabe
Wouldn't that require Create to be virtual?
gabr
@gabr: Only when the derived class has its own create. In that case the base Create has to be virtual and the derived must override it. The use of ClassType only guarantees that the correct class is instantiated - the user has to take care that the correct Create is called.
Uwe Raabe
The constructor needs to be declared virtual, otherwise the constructor code will not be executed: http://stackoverflow.com/questions/791069/
mjustin
@mjustin: That is a complete different case, because Clazz is declared as TClass. So only TObject.Create is executed. This would be the same as I would write result := ClassType.Create, but that will give a type mismatch. The typecast guarantees that in my example TMyClass.Create will be executed.
Uwe Raabe