Jak przekazać wskaźnik do nowego komponentu?

0

Witam, jestem nowy na forum i zaczynam uczyć się dopiero Delphi.
Mam zadanie zrobić listę jednokierunkową korzystając ze wskaźników i klas lub rekordów.
Wygląda to tak:

Wsk = ^pacjet;
pacjet = record
  imie: String;
  nazwisko: String;
  diagnoza: String;
  next: Wsk;
end;

Wszystko mi działa, ale muszę zrobić jeszcze własny komponent. W komponencie jest jedna procedura, która zlicza ilość osób chorych z rekordu diagnoza. Do użycia tego komponentu potrzebuję dostępu do tej listy lub wskaźnika. I właśnie nie wiem jak to zrobić. Gdy przenoszę Wsk=^pacjet do zmiennych publicznych to nie widzi mi go w dalszej części programu. Proszę o pomoc i z góry dziękuję.

0

kod wstawiaj w odpowiednie znaczniki. Napisz dokładnie jak tworzysz ten wskaźnik i jak go przekazujesz.

0

Nie wiem, czy skumałem o co dokładnie ci chodzi, ale tu masz przykład użycia komponentu i wskaźników:

 type
   Wsk = ^pacjent;
   pacjent = record
               imie: String;
               nazwisko: String;
               diagnoza: String;
               next: Wsk;
             end;

   TMojKomponent = class(TComponent)
     private
        FMKWsk: Wsk; //pole komponentu przechowujące wskaźnik podany jako parametr metody "Wyswietl"
     public
       procedure Wyswietl(w: Wsk); //metoda komponentu, która coś tam robi z danymi przekazanymi wskaźnikiem "w"
       property MKWsk: Wsk read FMKWsk write FMKWsk; //property zapewniające dostęp do pola FMKWsk komponentu (odczyt i modyfikacja)
     end;

Var
 W: Wsk;
 MojKomponent: TMojKomponent;

implementation

{$R *.dfm}

procedure TMojKomponent.Wyswietl(w: Wsk); //metoda komponentu, która coś tam robi z danymi przekazanymi wskaźnikiem "w"
begin
  FMKWsk := w; //zapamiętanie wskaźnika przez komponent
  ShowMessage('Imie wyświetlone w metodzie "Wyswietl" komponentu: '+w^.imie);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  New(W); //utworzenie nowej zmiennej dynamicznej
  MojKomponent := TMojKomponent.Create(Self); //utworzenie komponentu
  try
    W^.imie := 'Ferdek'; //przypisanie przykładowego imienia
    ShowMessage('Imie odczytane z W: ' + W^.imie);

    MojKomponent.wyswietl(W); //wykonanie metody komponentu z użyciem wskaźnika "W"
    ShowMessage('Imie odczytane z komponentu: '+MojKomponent.MKWsk^.imie); //tu komponent użyje swojego zapamiętanego wskaźnika (bo property MojKomponent.MKWsk (czyli też pole FMKWsk) wskazuje na "W")

    MojKomponent.MKWsk^.imie := 'Boczek'; //imie zmienione przez komponenent (przez użycie property MKWsk)
    ShowMessage('Imie po zmianie przez komponenent, odczytane z W: ' + W^.imie);
  finally
    Dispose(W); //zwolnienie zmiennej dynamicznej z pamięci
    MojKomponent.Free; //zwolnienie komponentu z pamięci
  end;
end;
 
0

Przykład działa jak chcesz chyba? Komponent od momentu użycia metody Wyswietl będzie zwracał poprawny wskaźnik na przekazany rekord z właściwości MKWsk co zresztą udowodniłeś "Boczkiem". Pamiętać tylko musisz, że po wyjściu z Button1Click zostanie zwolniona pamięć pod tym wskaźnikiem (blok finally) i wszystkie pozostałe wskaźniki będą wskazywać na złe (zwolnione) miejsce w pamięci, ale to i tak nie ma znaczenia bo w tym samym miejscu zwalniasz sam komponent.
Pisałeś o rekordzie diagnoza z którym masz problem, ale tu takiego nie ma.

0

Dzięki za porady. Zrobiłem podobnie do @marogo. Mój komponent wygląda tak:

 
unit IloscChorych;

interface

uses
  System.SysUtils, System.Classes, Vcl.Controls, Vcl.StdCtrls;

 type
   Wsk = ^pacjet;

  pacjet = record
    Imie: string;
    Nazwisko: string;
    Data: string;
    Oddzial: string;
    Diagnoza: string;
    Next: Wsk;
  end;

type
  TIloscChorych = class(TLabel)
  private
  protected
  public
    procedure licz_chorych(root:Wsk);
  published
  end;

procedure Register;

var
temp : Wsk;
liczba_zdrowych, liczba_chorych : Integer;

implementation

procedure Register;
begin
  RegisterComponents('Standard', [TIloscChorych]);
end;


procedure TIloscChorych.licz_chorych(root:Wsk);
begin

  temp:=root;
  liczba_chorych:=0;
  liczba_zdrowych:=0;
  while temp<>Nil do
  begin

    if temp^.diagnoza='Chory' then
      begin
        liczba_chorych := liczba_chorych +1;
      end;

    if temp^.diagnoza='Zdrowy' then
      begin
        liczba_zdrowych := liczba_zdrowych +1;
      end

  end;
  Self.Caption := 'Chorych: ' + IntToStr(liczba_chorych) + #13#10 +'Zdrowych: ' + IntToStr(liczba_zdrowych);
end;
end.

W programie wywołuje procedurę:

 TIloscChorych.licz_chorych(Root);

Root to adres początku listy.
Ale to i tak nie chce zadziałać. Gdy kompiluję wywala błąd: [DCC Error] Pacjeci.pas(396): E2010 Incompatible types: 'IloscChorych.Wsk' and 'Pacjeci.Wsk'.
Już naprawdę nie wiem jak przekazać wartość Root do komponentu.

0

[DCC Error] Pacjeci.pas(396): E2010 Incompatible types: 'IloscChorych.Wsk' and 'Pacjeci.Wsk'.

Bo jak widać (albo i nie widać) są to różne typy;

Swoją drogą jeśli potrzebujesz koniecznie wykorzystać swoją implementację listy, to opakuj ją w klasę i tak z niej korzystaj - unikniesz wielu problemów; I niepotrzebnie pakujesz takie rzeczy do komponentu, który de facto służy do wyświetlania danych treści, a nie do obliczeń.

0

Wiem, że wydaje się to dziwne, ale takie mam zadanie i muszę zrobić jakiś komponent. Chciałem zrobić taki, który zliczy mi liczbę chorych z listy. Na inny nie mam pomysły. Masz może jakiś pomysł na komponent?

0

Moja klasa obsługi logów oparta na liście jednokierunkowej:
Lista jednokierunkowa za mała pojemność. StringGrid się zawiesza przy wczytaniu dużej ilości danych.

A tu jej zmodyfikowana (moim zdaniem lepsza) wersja:

unit Logs;

{==============================================================================}

{$mode objfpc}{$H+}

{==============================================================================}

interface

{==============================================================================}

uses
  Classes, SysUtils;

{==============================================================================}

// Typ określający priorytet logu.
//---------------------------
type
  TPriority = (pLowest, pLow, pNormal, pHigh, pHighest);

// Typ rekordowy określający co ma zawierać log.
//----------------------------------------------
type
  TLogData = packed record
    Time     : String[20];  // Czas w którym zapisano log.
    Msg      : String[100]; // Wiadomość logu.
    Priority : TPriority;   // Priorytet logu.
  end;

// Typ uzywany w klasie do budowania listy jednokierunkowej.
//----------------------------------------------------------
type
  PLogRec = ^TLogRec; // Typ wskaźnikowy.
  TLogRec = record
    Next : PlogRec;   // Wskaźnik na nastepny rekord na liście.
    Log  : TLogData;  // Główne dane logu.
  end;

{------------------------------------------------------------------------------}

// Klasa logów pomaga zapisywać logi z różnych prac aplikacji.
//------------------------------------------------------------
type
  TLogClass = class(TObject)
  private
    First    : PLogRec;          // Wskaźnik na pierwszy rekord w liście
    Last     : PLogRec;          // Wskaźnik na ostatni rekord w liście
    FileName : string;           // Nazwa pliku do którego klasa zapisuje logi
    LogFile  : file of TLogData; // Plik do którego klasa zapisuje logi
    Count    : QWord;            // Ilość logów na liście.
    Max      : QWord;            // Maksymalna ilość logów na liście.
  public
    constructor Create(aBuff : QWord);
    destructor Destroy; override;

    procedure Add(aMsg : String; aPriority : TPriority);
    function GetFirst : PLogRec;
    procedure Clear;
    procedure SaveToFile;
  end;

{==============================================================================}

implementation

{==============================================================================}

// Konstruktor klasy. Nadaje wartości polom FileName (aktualna data i godzina),
// First, Last, Max, Count oraz przygotowuje plik logów do zapisu.
//------------------------------------------------------------------------------
constructor TLogClass.Create(aBuff : QWord);
begin
  inherited Create;
  FileName := StringReplace(DateTimeToStr(now), '-', '.', [rfReplaceAll]);
  FileName := StringReplace(FileName, ':', '-', [rfReplaceAll]);
  FileName := FileName + '.log';
  AssignFile(LogFile, FileName);
  Rewrite(LogFile);
  First := nil;
  Last := nil;
  Max := aBuff;
  Count := 0;
end;

{------------------------------------------------------------------------------}

// Destruktor klasy. Zapisuje listę do pliku, czyści ją a następnie zamyka plik.
//------------------------------------------------------------------------------
destructor TLogClass.Destroy;
begin
  if First <> nil then
  begin
    SaveToFile;
    Clear;
  end;
  CloseFile(LogFile);
  inherited Destroy;
end;

{------------------------------------------------------------------------------}

// Dodaje log "aMsg" o priorytecie "aPriority" do listy.
// Jesli lista się przepełni jest zapisywana do pliku, a nastepnie czyszczona.
//----------------------------------------------------------------------------
procedure TLogClass.Add(aMsg : String; aPriority : TPriority);
var
  NewLog : PLogRec;
begin
// Tworzenie nowego rekordu do listy
  New(NewLog);
  NewLog^.Log.Msg := aMsg;
  NewLog^.Log.Priority := aPriority;
  NewLog^.Log.Time := DateTimeToStr(Now);
  NewLog^.Next := nil;
// Zwiększenie o 1 zmiennej przechowującej ilość logów na liście
  Inc(Count);
// Dodanie logu na koniec listy.
  if First = nil then
  begin
    First := NewLog;
    Last := First;
  end
  else
  begin
    Last^.Next := NewLog;
    Last := NewLog;
  end;
// Sprawdzanie czy lista nie jest pełna.
// Jeśli tak to zapisz listę do pliku, wyczyść i ustaw ilość na 0.
  if Count = Max then;
  begin
    SaveToFile;
    Clear;
    Count := 0;
  end;
end;

{------------------------------------------------------------------------------}

// Zwraca wskaźnik na pierwszy element listy.
//-------------------------------------------
function TLogClass.GetFirst : PLogRec;
begin
  result := First;
end;

{------------------------------------------------------------------------------}

// Czyści listę
//-------------
procedure TLogClass.Clear;
var
  ToDelete : PLogRec;
begin
  while First <> nil do
  begin
    ToDelete := First;
    First := First^.Next;
    Dispose(ToDelete);
  end;
  inherited Destroy;
end;

{------------------------------------------------------------------------------}

// Zapisuje listę do pliku
//------------------------
procedure TLogClass.SaveToFile;
var
  ToSave : PLogRec;
begin
  ToSave := GetFirst;
  while ToSave <> nil do
  begin
    Write(LogFile, ToSave^.Log);
    ToSave := ToSave^.Next
  end;
end;

{==============================================================================}

end.

Idealna nie jest ale spełnia w 100% swoje zadanie. Przy okazji sugestie dotyczące zmian/optymalizacji mile widziane w komentarzach ;)
I tak wiem, że miałem zmienić

FileName := StringReplace(DateTimeToStr(now), '-', '.', [rfReplaceAll]);
FileName := StringReplace(FileName, ':', '-', [rfReplaceAll]);

na FormatDateTime ale najpierw chce zrozumieć tą funkcję a dopiero potem jej używać a ostatnio nie mam czasu na kodzenie.

1
kalhusak napisał(a):

Dzięki za porady. Zrobiłem podobnie do @marogo. Mój komponent wygląda tak:
W programie wywołuje procedurę:

 TIloscChorych.licz_chorych(Root);

Root to adres początku listy.

Ale to i tak nie chce zadziałać. Gdy kompiluję wywala błąd: [DCC Error] Pacjeci.pas(396): E2010 Incompatible types: 'IloscChorych.Wsk' and 'Pacjeci.Wsk'.
Już naprawdę nie wiem jak przekazać wartość Root do komponentu.

Wygląda na to, że typ "Wsk" zadeklarowałeś nie tylko w module "IloscChorych", ale też jego klon w (głównym?) module programu "Pacjeci", stąd ten konflikt.

Po prostu wywal deklarację tego typu z modułu "Pacjeci", bo skoro na liście "uses" tego modułu jest moduł "IloscChorych", to dzięki temu moduł "Pacjeci" będzie
"znał" typ "Wsk".

A tak poza tym, to co to za wywołanie metody "licz_chorych":

TIloscChorych.licz_chorych(Root); 

Jak już, to jeśli położyłeś komponent "TIloscChorych" na formatce modułu "Pacjeci", to wywołanie metody "licz_chorych" powinno wyglądać np. tak:

Var
 IloscChorych1: TIloscChorych; //to ci się samo dopisało w definicji klasy formatki głównej

 IloscChorych1.licz_chorych(Root);

A jeśli tworzysz komponent dynamicznie (nie położyłeś go na formatce tego modułu "Pacjeci"):

Var
 IloscChorych: TIloscChorych;

IloscChorych:=TIloscChorych.Create;
try
  IloscChorych.licz_chorych(Root);
  (...)
finally
  IloscChorych.Free;
end;
0
marogo napisał(a)

A tak poza tym, to co to za wywołanie metody "licz_chorych":

TIloscChorych.licz_chorych(Root);

Jako ciekawostka - można by było w ten sposób wywołać metodę poprawnie, ale trzeba ją zrobić metodą statyczną klasy:

type
  TIloscChorych = class(TLabel)
  public
    class procedure licz_chorych(root:Wsk); static;
  end;

Wtedy będzie można z niej korzystać bez tworzenia instancji klasy w pamięci (za pomocą konstruktora):

TIloscChorych.licz_chorych(Root); //nie spowoduje stworzenia wyjątku EAccessViolation

1 użytkowników online, w tym zalogowanych: 0, gości: 1