Domanda

Qualcosa mi sono chiesto per molto tempo: perché non sono i record di Delphi in grado di avere l'eredità (e quindi tutte le altre caratteristiche OOP importanti)

Questo sarebbe essenzialmente fare dischi la versione stack allocato di classi, proprio come classi C ++, e renderebbe "oggetti" (nota: non istanze) obsoleti. Non vedo nulla di problematico con esso. Questo sarebbe anche una buona occasione per implementare le dichiarazioni previsionali per i record (che sto ancora sconcertato per spiegare perché è ancora mancante).

Non si vede alcun problema con questo?

È stato utile?

Soluzione

Rilevante per questa domanda, ci sono due tipi di eredità:. Ereditarietà di interfaccia e l'ereditarietà dell'implementazione

eredità Interfaccia implica generalmente il polimorfismo. Ciò significa che se B è derivato da A, quindi valori di tipo B possono essere memorizzati in posizioni di tipo A. Questo è problematico per i tipi di valore (come record) rispetto al riferimento tipi, a causa di affettatura. Se B è più grande di A, quindi riporlo in un luogo di tipo A troncherà il valore -. Tutti i campi che B aggiunto nella sua definizione oltre a quelli di A saranno persi

Attuazione ereditarietà è meno problematico da questo punto di vista. Se Delphi aveva eredità record, ma solo l'attuazione, e non l'interfaccia, le cose non sarebbero troppo male. L'unico problema è che semplicemente facendo un valore di tipo A un campo di tipo B non la maggior parte di ciò che si vorrebbe di ereditarietà dell'implementazione.

L'altra questione è metodi virtuali. metodo virtuale spedizione richiede un certo tipo di tag per-valore per indicare il tipo di esecuzione del valore, in modo che il metodo override corretta può essere scoperto. Ma i record non hanno alcun posto di memorizzare questo tipo: i campi del record è tutti i campi che ha. Oggetti (il vecchio Turbo Pascal tipo) possono avere metodi virtuali perché hanno un VMT: il primo oggetto nella gerarchia di definire un metodo virtuale aggiunge implicitamente un VMT alla fine della definizione dell'oggetto, crescendo esso. Ma gli oggetti Turbo Pascal hanno lo stesso problema affettare sopra descritta, che li rende problematico. metodi virtuali su tipi di valore richiede in modo efficace l'ereditarietà di interfaccia, il che implica il problema di taglio.

Quindi, al fine di sostenere adeguatamente l'interfaccia record di eredità correttamente, avremmo bisogno qualche tipo di soluzione al problema affettare. Boxe sarebbe un tipo di soluzione, ma richiede in genere la raccolta dei rifiuti per essere utilizzabile, e introdurrebbe l'ambiguità nella lingua, in cui potrebbe non essere chiaro se si sta lavorando con un valore o un riferimento - un po 'come Integer vs int in Java con autoboxing. Almeno in Java ci sono nomi distinti per la scatola vs unboxed "tipi" di tipi di valore. Un altro modo di fare la boxe è come Google Go con le sue interfacce, che è una sorta di eredità di interfaccia senza ereditarietà dell'implementazione, ma richiede le interfacce da definire a parte, e tutte le sedi di interfaccia sono riferimenti. tipi di valore (ad esempio record) vengono inscatolati quando cui da un riferimento interfaccia. E, naturalmente, Go ha anche la raccolta dei rifiuti.

Altri suggerimenti

Dati storici e Classi / oggetti sono due cose molto diverse a Delfi. Fondamentalmente un record di Delphi è una struct C - Delphi supporta anche la sintassi di fare le cose come avere un record che può essere letta sia come 4 numeri interi a 16 bit o un 2 numeri interi a 32 bit. Come struct, record risale a prima programmazione orientata agli oggetti è entrata nel linguaggio (ERA Pascal).

Come una struct un record è anche un pezzo in linea di memoria, non un puntatore a un pezzo di memoria. Ciò significa che quando si passa un record in una funzione, si passa una copia, non un puntatore / riferimento. Significa anche che quando si dichiara un tipo di variabile record nel codice, viene determinato al momento della compilazione quanto è grande - le variabili di tipo di record utilizzati in una funzione saranno assegnati sullo stack (non come un puntatore sullo stack, ma come 4, 10, 16, ecc struttura byte). Questa dimensione fissa non gioca bene con il polimorfismo.

L'unico problema che vedo (e ho potuto essere miopi o sbagliata) è lo scopo. Record sono per memorizzare dati mentre gli oggetti vengono per la manipolazione e l'utilizzo di detti dati. Perché un armadio di immagazzinaggio ha bisogno di routine di manipolazione?

Hai ragione, aggiungendo eredità ai record sarebbe in sostanza, li trasformano in classi C ++. E questa è la risposta proprio lì: non è fatto perché sarebbe una cosa orribile da fare. Si possono avere i tipi di valore dello stack-assegnati, oppure si può avere classi e gli oggetti, ma mescolando i due è una pessima idea. Una volta fatto, si finisce con tutti i tipi di problemi di gestione del ciclo di vita e finisce per dover costruire brutte hack come modello RAII C ++ s 'nella lingua, al fine di trattare con loro.

Linea di fondo: se si desidera un tipo di dati che può essere ereditato e ampliato, classi d'uso. Questo è quello che stanno lì.

EDIT: In risposta alla domanda di Cloud, questo non è davvero qualcosa che può essere dimostrato mediante un unico semplice esempio. L'intera C ++ modello a oggetti è un disastro. Non può presentarsi come una da vicino; bisogna capire diversi problemi interconnessi di cogliere veramente il quadro generale. RAII è solo il disordine nella parte superiore della piramide. Forse scriverò una spiegazione più dettagliata sul mio blog fine di questa settimana, se ho il tempo.

A causa record non hanno VMT (tabella metodo virtuale).

Si potrebbe provare a utilizzare il Delphi oggetto parola chiave per questo. Quelli in fondo sono ereditabili, ma si comportano molto più simile record che per le classi.

filo e questo descrizione

In passato ho utilizzato oggetti (non classi!) Come record con l'ereditarietà.

A differenza di quello che alcune persone qui stanno dicendo che ci sono motivi legittimi per questo. Il caso ho fatto coinvolto due strutture da fonti esterne (API, non nulla dal disco - mi serviva il record completamente formato in memoria)., Il secondo dei quali solo il primo esteso

Questi casi sono molto rari, però.

Questo è il tema per la tua domanda e si riferisce ad estendere la funzionalità dei tipi di record e di classe via aiutanti di classe e di record. Secondo la documentazione di Embarcadero su questo si può estendere una classe o un record (ma senza sovraccarico operatore è supportato da aiutanti). Quindi, in pratica è possibile estendere le funzionalità in termini di metodi di membro, ma non ci sono dati membri). Essi supportano i campi di classe che si poteva accedere tramite getter e setter nel solito modo anche se non ho ancora testato questo. Se si voleva interfaccia di accesso ai dati della classe o registrare stavate aggiungendo il supporto per, probabilmente si potrebbe raggiungere questo obiettivo (cioè dell'attivazione di un evento o un segnale quando i dati membri della classe o record originale è stato modificato). Non si poteva implementare i dati che nasconde però ma permette di sovrascrivere una funzione membro esistente della classe originale.

ad es. Questo esempio funziona in Delphi XE4. Creare un nuovo form VCL Applicazione e sostituire il codice dal Unit1 con il seguente codice:

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, System.Types;

type

  TMyArray2D = array [0..1] of single;

  TMyVector2D = record
  public
    function Len: single;
    case Integer of
      0: (P: TMyArray2D);
      1: (X: single;
          Y: single;);
  end;

  TMyHelper = record helper for TMyVector2D
    function Len: single;
  end;


  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;


implementation

function TMyVector2D.Len: Single;
begin
  Result := X + Y;
end;

function TMyHelper.Len: single;
begin
  Result := Sqrt(Sqr(X) + Sqr(Y));
end;

procedure TestHelper;
var
  Vec: TMyVector2D;
begin
  Vec.X := 5;
  Vec.Y := 6;
  ShowMessage(Format('The Length of Vec is %2.4f',[Vec.Len]));
end;

procedure TForm1.Form1Create(Sender: TObject);
begin
  TestHelper;
end;

Si noti che il risultato è 7,8102 anziché 11. Questo dimostra che è possibile nascondere i metodi membri della classe originaria o registrare con una classe o un record di supporto.

In un certo senso si sarebbe solo trattare l'accesso ai membri di dati originali proprio come si farebbe in modifica dei valori dall'interno l'unità in cui una classe è dichiarata cambiando attraverso le proprietà piuttosto che i campi direttamente in modo che le azioni appropriate sono prese dal getter e setter di tali dati.

Grazie per la domanda. Io certamente imparato molto nel tentativo di trovare la risposta e mi ha aiutato un grande affare troppo.

Brian Joseph Johns

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top