dynamiczne tworzenie formularzy

dział ogólny

dynamiczne tworzenie formularzy

Nowy postprzez banita » niedziela, 18 stycznia 2009, 02:40

projektujemy sobie formularz ale nie chcemy aby byl tworzony na starcie tylko puzniej dynamicznie przydzielimy mu pamiec.
ustawiamy w opcjach projektu aby nie tworzyc formularza autoamtycznie.
zalozmy ze rozpatrujemy formularz TForm2, instancja *Form2.
puzniej pod jakims ONClick mamy kod twozacy formularz. i tu jest moje pytanie:
czym rozni sie kod:
Kod: Zaznacz cały
Form2 = new TForm2(this);

od tego ktory builder generuje automatycznie(w pliku z WinMain) dla form ktore on tworzy czyli:
Kod: Zaznacz cały
Application->CreateForm(__classid(TForm2), &Form2);


oba te kody utworza nam egzemplarz tego formularza, jaka jest wiec roznica w ich dzialaniu i z ktorego nalezy kozystac?
Avatar użytkownika
banita
Kreacjusz
Kreacjusz
 
Posty: 283
Dołączył(a): poniedziałek, 28 lipca 2008, 20:07
Podziękował : 1
Otrzymał podziękowań: 18
System operacyjny: Windows 7 Professional
Kompilator: C++Builder 2010 Update 5,
Delphi 2010 Update 5,
NetBeans 6.9(MinGw + Qt),
Visual Studio 2008 + Qt
Gadu Gadu: 0
    NieznanyNieznana

Re: dynamiczne tworzenie formularzy

Nowy postprzez Cyfrowy Baron » niedziela, 18 stycznia 2009, 09:48

► 


Upraszcza procedurę tworzenia formularza. Nie musisz tworzyć całego tego kodu związanego z dynamicznym tworzeniem formularza, gdyż funkcja CreateForms tworzy go za ciebie. Sam zdecyduj co wolisz.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    NieznanyNieznana

Re: dynamiczne tworzenie formularzy

Nowy postprzez banita » niedziela, 18 stycznia 2009, 14:00

chyba sie nie zrozumielismy. w helpie pisz ze programista nie musi twozyc nowych form dynamicznie bo funkcja CreateForm zrobi to za niego sama (czyli chodzi o opcje projektu dla ktorych form jest na auto create). mi chodzi o sytuacje gdy formularz nie jest kreowany automatycznie tylko tworzymy go sami i tutaj roniwerz mozemy wywolac operator new lub funkce create form. w obu przypadka ten sam naklad kodu, w obu przypadkach puzniej trzeba wywplac delete wiec jaka jest roznica w ich uzyciu.
Avatar użytkownika
banita
Kreacjusz
Kreacjusz
 
Posty: 283
Dołączył(a): poniedziałek, 28 lipca 2008, 20:07
Podziękował : 1
Otrzymał podziękowań: 18
System operacyjny: Windows 7 Professional
Kompilator: C++Builder 2010 Update 5,
Delphi 2010 Update 5,
NetBeans 6.9(MinGw + Qt),
Visual Studio 2008 + Qt
Gadu Gadu: 0
    NieznanyNieznana

Re: dynamiczne tworzenie formularzy

Nowy postprzez Cyfrowy Baron » niedziela, 18 stycznia 2009, 14:28

Fakt, mój błąd, nie zrozumieliśmy się. Dla Twoich celów jest to zupełnie zbędne gdyż ta funkcja odpowiada za automatyczne tworzenie formularza, czyli dotyczy formularzy widocznych na Auto-create form.

Jeżeli na Auto-create form umieścisz zarówno Frm1 jak i Form 2, otrzymasz w pliku źródłowym projektu (nie pliku źródłowym formualrz) taki kod:

Kod: Zaznacz cały
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop
//---------------------------------------------------------------------------
USEFORM("Unit1.cpp", Form1);
USEFORM("Unit2.cpp", Form2);
//---------------------------------------------------------------------------
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
   try
   {
      Application->Initialize();
      SetApplicationMainFormOnTaskBar(Application, true);
      Application->CreateForm(__classid(TForm1), &Form1); // <---------
      Application->CreateForm(__classid(TForm2), &Form2); // <---------
      Application->Run();
   }
   catch (Exception &exception)
   {
      Application->ShowException(&exception);
   }
   catch (...)
   {
      try
      {
         throw Exception("");
      }
      catch (Exception &exception)
      {
         Application->ShowException(&exception);
      }
   }
   return 0;
}
//---------------------------------------------------------------------------



Jeżeli natomiast umieścisz Form2 na Available form, otrzymasz taki kod:

Kod: Zaznacz cały
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop
//---------------------------------------------------------------------------
USEFORM("Unit1.cpp", Form1);
USEFORM("Unit2.cpp", Form2);
//---------------------------------------------------------------------------
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
   try
   {
      Application->Initialize();
      SetApplicationMainFormOnTaskBar(Application, true);
      Application->CreateForm(__classid(TForm1), &Form1); // <---
      Application->Run();
   }
   catch (Exception &exception)
   {
      Application->ShowException(&exception);
   }
   catch (...)
   {
      try
      {
         throw Exception("");
      }
      catch (Exception &exception)
      {
         Application->ShowException(&exception);
      }
   }
   return 0;
}
//---------------------------------------------------------------------------


CreateForm tworzy automatycznie formularz w oparciu klasę bazową, dlatego gdy tworzysz np. formularz główny aplikacji (np. Unit1 - Form1) nie musisz tworzyć go z wykorzystaniem operatora new, gdyż kompilator umieszcza automatycznie w pliku źródłowym projektu (np. Project.cpp) instrukcję: Application->CreateForm(__classid(TForm1), &Form1); nakazującą utworzenie formularza wraz z uruchomieniem aplikacji. Nie wiem jak to prościej wytłumaczyć.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    NieznanyNieznana

Re: dynamiczne tworzenie formularzy

Nowy postprzez banita » niedziela, 18 stycznia 2009, 15:15

no ale to ja to wiem:)
moze jeszcze inaczej:), taki przyklad: w form1 pod onclick button1 mam taki kod:

Kod: Zaznacz cały
Form2 = new TForm2(this);
Form2->ShowModal();
delete Form2;
Form2 = 0;

lub taki kod:
Kod: Zaznacz cały
Application->CreateForm(__classid(TForm2), &Form2);
Form2->ShowModal();
delete Form2;
Form2 = 0;


oba zadzialaja tak samo. wiec ktory powinno sie stosowac.
Avatar użytkownika
banita
Kreacjusz
Kreacjusz
 
Posty: 283
Dołączył(a): poniedziałek, 28 lipca 2008, 20:07
Podziękował : 1
Otrzymał podziękowań: 18
System operacyjny: Windows 7 Professional
Kompilator: C++Builder 2010 Update 5,
Delphi 2010 Update 5,
NetBeans 6.9(MinGw + Qt),
Visual Studio 2008 + Qt
Gadu Gadu: 0
    NieznanyNieznana

Re: dynamiczne tworzenie formularzy

Nowy postprzez Cyfrowy Baron » niedziela, 18 stycznia 2009, 15:37

Pierwszy sposób, gdyż drugi jest zarezerwowany dla automatycznego tworzenia formularzy.
Poza tym w przypadku pierwszego formularza możesz tworzyć więcej niż jeden formularz w oparciu o klasę bazową ( w przykładzie Form2):

Kod: Zaznacz cały
TForm2 *Form2 = new TForm2(this);
Form2->ShowModal();
delete Form2;
Form2 = 0; // to jest zbędne

TForm2 *MyForm = new TForm2(this);
MyFrm->ShowModal();
delete MyForm;


Nie da się tego samego zrobić z funkcją CreateForm.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    NieznanyNieznana

Re: dynamiczne tworzenie formularzy

Nowy postprzez banita » niedziela, 18 stycznia 2009, 15:53

i o taka odpowiedz mi chodzilo:)

czemu przypisanie do zwlnionego wskaznika zera jest zbedne? w sumie to dobra praktyka ktora nic nie kosztuje a nieraz moze uratowac od bledow ktore sa praktycznie nie do wylapamia. nawet jesli borland sam dopisuje tam zero czy cos to w innych kompilatorach tego nie ma wiec moim zdaniem jest to dobra praktyka. zreszta tak ucza najlepsi znawcy C++ jak herb sutter czy scot meyers:P
Avatar użytkownika
banita
Kreacjusz
Kreacjusz
 
Posty: 283
Dołączył(a): poniedziałek, 28 lipca 2008, 20:07
Podziękował : 1
Otrzymał podziękowań: 18
System operacyjny: Windows 7 Professional
Kompilator: C++Builder 2010 Update 5,
Delphi 2010 Update 5,
NetBeans 6.9(MinGw + Qt),
Visual Studio 2008 + Qt
Gadu Gadu: 0
    NieznanyNieznana

Re: dynamiczne tworzenie formularzy

Nowy postprzez Cyfrowy Baron » niedziela, 18 stycznia 2009, 16:38

operator delete zwalnia pamięć. Przypisanie wartości 0 do tego co już zero zawiera wydaje mi się zbędne.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    NieznanyNieznana

Re: dynamiczne tworzenie formularzy

Nowy postprzez banita » niedziela, 18 stycznia 2009, 16:44

przecierz operator delete (nie przeciazony!) zwalnia pamiec ale nie przypisuje zera tylko zostaje ten adres ktory byl zarezerwowany! pozniej przypadkowa proba ponownego zwolnienia takiego wskaznika daje efekt niezdefiniowany. jesli przypiszemy do niego 0 to ponowne zwolnienie nie da efektow niepozadanych.
Avatar użytkownika
banita
Kreacjusz
Kreacjusz
 
Posty: 283
Dołączył(a): poniedziałek, 28 lipca 2008, 20:07
Podziękował : 1
Otrzymał podziękowań: 18
System operacyjny: Windows 7 Professional
Kompilator: C++Builder 2010 Update 5,
Delphi 2010 Update 5,
NetBeans 6.9(MinGw + Qt),
Visual Studio 2008 + Qt
Gadu Gadu: 0
    NieznanyNieznana

Re: dynamiczne tworzenie formularzy

Nowy postprzez Cyfrowy Baron » niedziela, 18 stycznia 2009, 17:42

Jest dokładnie na odwrót. Użycie delete zwalnia pamięć i dlatego nie można ponownie użyć operatora delete, przypisanie 0 nie wiem co w zasadzie robi, gdyż nigdy nie zetknąłem się z czymś podobnym, prawdopodobnie w jakiś sposób rezerwuje pamięć i dlatego można ponownie użyć delete.

Kod: Zaznacz cały
TForm2 *MyForm = new TForm2(this);
MyForm->ShowModal();
delete MyForm;
MyForm =  0;
delete MyForm; // <-- ponowne użycie delete


Jeżeli możliwe jest ponowne użycie delete do usunięcia obiektu z pamięci, to jest logiczne, że ten obiekt musi znajdować się w pamięci.

Kod: Zaznacz cały
TForm2 *MyForm = new TForm2(this);
MyForm->ShowModal();
delete MyForm;
delete MyForm; // < -- to wywoła błąd, gdyż obiekt MyForm został już usunięty i zwolnił pamięć


w powyższym przykładzie nie można użyć dwukrotnie operatora delete, gdyż obiekt MyForm został już usunięty z pamięci i zwolnił adres, więc operator delete nie może go odnaleźć.




Kod: Zaznacz cały
TForm2 *MyForm = new TForm2(this);
MyForm->ShowModal();
delete MyForm;
MyForm = 0;
MyForm->ShowModal();


Powyższy kod wywoła komunikat błędu: Acces violation at address 00401A94 in module "Project1.exe". Read of address 00000000

Kod: Zaznacz cały
TForm2 *MyForm = new TForm2(this);
MyForm->ShowModal();
delete MyForm;
MyForm->ShowModal();


A ten kod zaowocuje komunikatem błędu: Acces violation at address 00000000. Read of address 00000000

To chyba najdobitniej świadczy o tym, że operator delete zwalnia adres w pamięci, a przypisanie oknu wartości 0 umieszcza okno pod jakimś adresem.



Użycie Form2 = 0 wydaje mi się mieć tylko uzasadnienie w sytuacji gdy nie niszczysz obiektu za pomocą operatora delete, lecz gdy obiekt sam siebie niszczy w zdarzeniu OnClose, czyli umieszczasz kod niszczenia obiektu i zwalniania pamięci w zdarzeniu OnClose formularza bazowego, np:

na Form1 wywołujesz MyForm w oparciu o klasę bazową TForm2:
Kod: Zaznacz cały
TForm2 *MyForm = new TForm2(this);
MyForm->Show(); // nie musi być modalne


następnie w klasie bazowej w zdarzeniu OnClose formularza Form2 umieszczasz taki kod:

Kod: Zaznacz cały
//---------------------------------------------------------------------------
void __fastcall TForm2::FormClose(TObject *Sender, TCloseAction &Action)
{
Action = caFree;
Form2 = 0;
}
//---------------------------------------------------------------------------




Kolejna ciekawostka, jeżeli np. w zdarzeniu OnClick przycisku Button1 umieścisz taki kod:

Kod: Zaznacz cały
TForm2 *MyForm = new TForm2(this);
MyForm->Show(); // nie musi być modalne


i klikniesz na niego np 3 razy, to zostaną utworzone 3 na pozór identyczne okna, jednak będą się różniły nazwą formularz, czyli będą miały kolejne nazwy: Form2, Form2_1, Form2_2, itd...

Jak to sprawdzić. Umieść na formularzu Form2 obiekt Label1, a następnie w zdarzeniu OnClick przycisku Button1 na formularzu Form1 umieść taki kod:

Kod: Zaznacz cały
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
TForm2 *MyForm = new TForm2(this);
MyForm->Show();
MyForm->Label1->Caption = MyForm->Name;
}
//---------------------------------------------------------------------------


Dzięki temu, że program sam zmienia nazwy kolejnych okien możesz się odwoływać do kolejnych okien po ich różnych nazwach. Jeżeli umieścisz do tego podany wyżej kod w zdarzeniu OnClose formularza bazowego, nie będziesz musiał się martwić wyciekiem pamięci, gdyż okn przy zamknięciu zawsze zwolni pamięć.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    NieznanyNieznana

Re: dynamiczne tworzenie formularzy

Nowy postprzez Witold » niedziela, 18 stycznia 2009, 19:17

Cyfrowy Baron napisał(a):Poza tym w przypadku pierwszego formularza możesz tworzyć więcej niż jeden formularz w oparciu o klasę bazową ( w przykładzie Form2):
Nie da się tego samego zrobić z funkcją CreateForm.


Sprawdziłeś ? U mnie tworzą się nowe formy tak jak w przypadku new.

Kod: Zaznacz cały
    TForm1 *Form12;
    Application->CreateForm(__classid(TForm1), &Form12);
    Form12->Show();
Avatar użytkownika
Witold
Konstrukcjonista
Konstrukcjonista
 
Posty: 223
Dołączył(a): piątek, 29 sierpnia 2008, 10:53
Podziękował : 1
Otrzymał podziękowań: 14
Kompilator: bcb6, Turbo C++ Explorer
    NieznanyNieznana

Re: dynamiczne tworzenie formularzy

Nowy postprzez banita » niedziela, 18 stycznia 2009, 20:07

cyfrowy baronie nie masz racji:)
standard c++ mowi ze dla adresu dla ktorego wywolano operator delete nie mozna wywolac go powtornie bo efekt bedzie niezdefiniowany. wyjatkiem od tej reguly jest adres zerowy(program nie moze tam miec swoich danych i nigdy nie ma) dla ktorego mozna wiele razy wywolac operator delete i operacja ta nie da zadnego efektu. dlatego po zwolnieniu pamieci dobrze jest przypisac zero aby na wypadek ponownego zwolnienia nie dostac beldu ktory puzniej jest w zasadzie nie do wykrycia. przypisanie zera nie powoduje zadnej alokacji ani nic z tych spraw.

poprostu adres zerowy jest ... wyjatkowy:)
Avatar użytkownika
banita
Kreacjusz
Kreacjusz
 
Posty: 283
Dołączył(a): poniedziałek, 28 lipca 2008, 20:07
Podziękował : 1
Otrzymał podziękowań: 18
System operacyjny: Windows 7 Professional
Kompilator: C++Builder 2010 Update 5,
Delphi 2010 Update 5,
NetBeans 6.9(MinGw + Qt),
Visual Studio 2008 + Qt
Gadu Gadu: 0
    NieznanyNieznana

Re: dynamiczne tworzenie formularzy

Nowy postprzez polymorphism » niedziela, 18 stycznia 2009, 21:26

dlatego po zwolnieniu pamieci dobrze jest przypisac zero aby na wypadek ponownego zwolnienia nie dostac beldu ktory puzniej jest w zasadzie nie do wykrycia.

Częściowo masz rację, ale w przypadku, który podałeś nie ma to zastosowania - zerujesz wskaźnik lokalny, który znika po wyjściu z metody, więc nie jest żadnym zagrożeniem.

Zawsze można zrobić tak:
Kod: Zaznacz cały
auto_ptr<TForm2> MyForm(new TForm2(this));
MyForm->ShowModal();
MyForm.reset(); //<--- opcjonalnie, jeśli chcesz konkretnie w tym miejscu usunąć obiekt.

i kwestię zerowania wskaźników mieć w ... wiadomo gdzie 8-)
C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2156
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 200
System operacyjny: Windows 8.1
Windows 10
Linux Mint 21.1
Kompilator: Visual Studio
Visual Studio Code
MSYS2 (MinGW, clang)
g++
clang
Gadu Gadu: 0
    NieznanyNieznana

Re: dynamiczne tworzenie formularzy

Nowy postprzez banita » niedziela, 18 stycznia 2009, 22:40

tutaj jest kwestia zalezna od tego jak twozymy forme.
jesli twozymy forme wizualnie (a tak napisalem w pierwszym poscie) ale nie dajemy jej jako formy automatycznie kreowanej to i tak dostajemy globalny(jesli go nie chcemy musimy sami usunac ta deklaracje) wskaznik do tej formy tyle ze jest on nie zainicjowany.

co do wskaznikow auto_ptr to w zasadzie nadaja sie one tylko i wylacznie do zastosowania w bloku try(do tego zreszta zostaly stworzone, aby odzyskac zasoby w trakcie zwijania stosu). w bcb jest klauzula __finally wiec w przypadku bcb ten szablon staje sie w zasadzie bezuzyteczny. jesli chcemy uzywac inteligentnych wskaznikow to najlepsze rozwiazanie to shared_ptr z biblioteki boost albo samemu napisac prosty szablon ze zliczaniem referencji.
Avatar użytkownika
banita
Kreacjusz
Kreacjusz
 
Posty: 283
Dołączył(a): poniedziałek, 28 lipca 2008, 20:07
Podziękował : 1
Otrzymał podziękowań: 18
System operacyjny: Windows 7 Professional
Kompilator: C++Builder 2010 Update 5,
Delphi 2010 Update 5,
NetBeans 6.9(MinGw + Qt),
Visual Studio 2008 + Qt
Gadu Gadu: 0
    NieznanyNieznana

Re: dynamiczne tworzenie formularzy

Nowy postprzez polymorphism » poniedziałek, 19 stycznia 2009, 00:18

co do wskaznikow auto_ptr to w zasadzie nadaja sie one tylko i wylacznie do zastosowania w bloku try(do tego zreszta zostaly stworzone, aby odzyskac zasoby w trakcie zwijania stosu). w bcb jest klauzula __finally wiec w przypadku bcb ten szablon staje sie w zasadzie bezuzyteczny.

No tu pojechałeś 8-) auto_ptr jest po to, żebyś nie musiał ręcznie zarządzać pamięcią. Czyli alokujesz pamięć i nie musisz się martwić o jej zwolnienie - chodzi o wykorzystanie automatyzmu takiego, jaki występuje podczas niszczenia obiektów lokalnych, tyle że w stosunku do obiektów stworzonych na stercie. Użycie auto_ptr w blokach try...catch jest tylko jednym z zastosowań. Zresztą takie ręczne zarządzanie pamięcią jest główną przyczyną wycieków, więc w tym przypadku absolutnie nie masz racji. Zrozumiesz to, jak zaczniesz pisać większe, bardziej zaawansowane projekty.

jesli chcemy uzywac inteligentnych wskaznikow to najlepsze rozwiazanie to shared_ptr z biblioteki boost

Mylisz się. Jeśli wskaźnik nie jest i nie będzie współdzielony, lepszym rozwiązaniem jest auto_ptr.
C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2156
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 200
System operacyjny: Windows 8.1
Windows 10
Linux Mint 21.1
Kompilator: Visual Studio
Visual Studio Code
MSYS2 (MinGW, clang)
g++
clang
Gadu Gadu: 0
    NieznanyNieznana

Następna strona

  • Podobne tematy
    Odpowiedzi
    Wyświetlone
    Ostatni post

Powrót do Ogólne problemy z programowaniem

Kto przegląda forum

Użytkownicy przeglądający ten dział: Brak zalogowanych użytkowników i 6 gości