Results 1 to 10 of 25

Thread: CLASS vs. OBJECT and memory management

Hybrid View

Previous Post Previous Post   Next Post Next Post
  1. #1
    In Delphi Objects are still available but are only meant for backward compatibility and their use is therefore not recommended.

    From Delphi documentation:
    Object types are supported for backward compatibility only. Their use is not recommended.
    Perhaps you might consider using what I call a "fake class approach". In this approach you are actually storing your data in array of records but instead of accessing that data directly from records you are accessing them with properties of a standardized class.
    Using this approach you have all of your data tightly stored in continuous memory blocks but still use advantages of classes. Here is a quick code example:

    Code:
    type
      TDirection = (dirLeft, dirRight);
    
      //Record definition for storing data of a sprite
      RSprite = record
        XPos: Single;
        YPos: Single;
        Width: Integer;
        Height: Integer;
        ImageIndex: Integer;
      end;
    
      //Array of sperite records to keep data stored in one continoud block of memory
      ASprites = Array of RSprite;
    
      //You can even split your data to multiple arrays keeping specific kinds of data
      //like position, helth status, etc.
      //This means that if for instance you need to loop through all characters to see
      //if any of them should be dead (zero or negative health) only data related to
      //health would be put on stack
      RHealthStatus = record
        HealthPoints: Integer;
        MaxHealthPoints: Integer;
        RegenerationRate: Integer;
      end;
    
      //Array of records containing unit health data
      AHelthStatus = Array of RHealthStatus;
    
      //Specialized class for accessing data.
      //Note that all properties are indexed properties since you need to tell which
      //sprite are you acessing
      TSprites = class(TObject)
      private
        FSprites: ASprites;
      protected
        function GetSpritesCount: Integer;
        function GetXPos(Index: Integer): Single;
        function GetYPos(Index: Integer): Single;
        function GetWidth(Index: Integer): Integer;
        function GetHeight(Index: Integer): Integer;
        function GetImageIndex(Index: Integer): Integer;
        procedure SetXPos(Index: Integer; const AXPos: Single);
        procedure SetYPos(Index: Integer; const AYPos: Single);
        procedure SetWidth(Index: Integer; const AWidth: Integer);
        procedure SetHeight(Index: Integer; const AHeight: Integer);
        procedure SetImageIndex(Index: Integer; const AImageIndex: Integer);
      public
        procedure Add(ASprite: RSprite);
        procedure Remove(SpriteIndex: Integer);
        constructor Create(AInitialSize: Integer);
        property SpritesCount: Integer read GetSpritesCount;
        property XPos[Index: Integer]: Single read GetXPos write SetXPos;
        property YPos[Index: Integer]: Single read GetYPos write SetYPos;
        property Width[Index: Integer]: Integer read GetWidth write SetWidth;
        property Height[Index: Integer]: Integer read GetHeight write SetHeight;
        property ImageIndex[Index: Integer]: Integer read GetImageIndex write SetImageIndex;
      end;
    
      //You can even subclass the base sprite class for adding additional functionality
      TMovableSprite = class(TSprites)
      protected
        function Move(Distance: Integer; Direction: TDirection): Boolean;
        //Inherited properties
        property SpritesCount;
        property XPos;
        property YPos;
        property Width;
        property Height;
        property ImageIndex;
      end;
    
      //You can even acces data from different specialized arrays of records
      TPlayer = class(TMovableSprite)
      private
        FHealthStatus: RHealthStatus;
      protected
        function GetHealthPoints: Integer;
        function GetMaxHealthPoints: Integer;
        function GetRegenerationRate: Integer;
        procedure SetHealthPoints(const AHealthPoints: Integer);
        procedure SetMaxHealthPoints(const AMaxHealthPoints: Integer);
        procedure SetRegenerationRate(const ARegenerationRate: Integer);
      public
        property HealthPoints: Integer read GetHealthPoints write SetHealthPoints;
        property MaxHealthPoints: Integer read GetMaxHealthPoints write SetMaxHealthPoints;
        property RegenarationRate: Integer read GetRegenerationRate write SetRegenerationRate;
        //Inherited properties
        property SpritesCount;
        property XPos;
        property YPos;
        property Width;
        property Height;
        property ImageIndex;
      end;
    
    implementation
    
    { TSprites }
    
    procedure TSprites.Add(ASprite: RSprite);
    begin
      SetLength(FSprites,Length(FSprites)+1);
    end;
    
    constructor TSprites.Create(AInitialSize: Integer);
    begin
      SetLength(FSprites, AInitialSize);
    end;
    
    function TSprites.GetHeight(Index: Integer): Integer;
    begin
      result := FSprites[Index].Height;
    end;
    
    function TSprites.GetImageIndex(Index: Integer): Integer;
    begin
      result := FSprites[Index].ImageIndex;
    end;
    
    function TSprites.GetSpritesCount: Integer;
    begin
      result := Length(FSprites);
    end;
    
    function TSprites.GetWidth(Index: Integer): Integer;
    begin
      result := FSprites[Index].Width;
    end;
    
    function TSprites.GetXPos(Index: Integer): Single;
    begin
      result := FSprites[Index].XPos;
    end;
    
    function TSprites.GetYPos(Index: Integer): Single;
    begin
      result := FSprites[Index].YPos;
    end;
    
    procedure TSprites.Remove(SpriteIndex: Integer);
    begin
      //code for removing object from array
    end;
    
    procedure TSprites.SetHeight(Index: Integer; const AHeight: Integer);
    begin
      FSprites[Index].Height := AHeight;
    end;
    
    procedure TSprites.SetImageIndex(Index: Integer; const AImageIndex: Integer);
    begin
      FSprites[Index].ImageIndex := AImageIndex;
    end;
    
    procedure TSprites.SetWidth(Index: Integer; const AWidth: Integer);
    begin
      FSprites[Index].Width := AWidth;
    end;
    
    procedure TSprites.SetXPos(Index: Integer; const AXPos: Single);
    begin
      FSprites[Index].XPos := AXPos;
    end;
    
    procedure TSprites.SetYPos(Index: Integer; const AYPos: Single);
    begin
      FSprites[Index].YPos := AYPos;
    end;
    
    { TMovableSprite }
    
    function TMovableSprite.Move(Distance: Integer; Direction: TDirection): Boolean;
    begin
      //Code for moving sprites
    end;
    
    { TPlayer }
    
    function TPlayer.GetHealthPoints: Integer;
    begin
      result := FHealthStatus.HealthPoints;
    end;
    
    function TPlayer.GetMaxHealthPoints: Integer;
    begin
      result := FHealthStatus.MaxHealthPoints;
    end;
    
    function TPlayer.GetRegenerationRate: Integer;
    begin
      result := FHealthStatus.Regenerationrate;
    end;
    
    procedure TPlayer.SetHealthPoints(const AHealthPoints: Integer);
    begin
      if AHealthPoints > FHealthStatus.MaxHealthPoints then
        FHealthStatus.HealthPoints := FHealthStatus.MaxHealthPoints
      else if AHealthPoints < 0 then
        FHealthStatus.HealthPoints := 0
      else
        FHealthStatus.HealthPoints := AHealthPoints;
    end;
    
    procedure TPlayer.SetMaxHealthPoints(const AMaxHealthPoints: Integer);
    begin
      FHealthStatus.MaxHealthPoints := AMaxHealthPoints;
    end;
    
    procedure TPlayer.SetRegenerationRate(const ARegenerationRate: Integer);
    begin
      FHealthStatus.Regenerationrate := ARegenerationRate;
    end;
    If you decide to go for splitting data into multiple arrays you need to make sure all arrays have same order of items (common index).

    This approach comes most useful when you are creating and destroying objects quote often since you don't suffer from class creation/destruction overhead.
    Another or its advantages is that because you are storing your data in arrays which are continuous memory blocks you can save them to or load them from files as such instead of needing to loop through a bunch of classes and save/load data for each of them separately.

    Notable drawbacks is the fact that if you store all the data in a single array the records that must be of the same type must have all the fields for all the data that most complex object stored in that array would need. This would mean that you might be wasting some space with more simpler objects whose data would not fill all the records fields.
    And if you go for multiple arrays approach maintaining data in them becomes more difficult.
    And probably the biggest drawback of such approach would be hard way to achieving good data reusability, even thou it is still doable.

    If you need to discus some more details of this design please let me know.
    Last edited by SilverWarior; 13-06-2016 at 06:58 PM.

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •