Analisis Struktur dan Penghancuran di Delphi
1 Model Objek di Delphi: 2
1.1 Apa arti nama objek? 2
1.2 Di mana objek disimpan? 2
1.3 Apa yang disimpan di objek? Bagaimana mereka disimpan?
2 Konstruktor dan Membuat Objek 5
2.1 Apa itu konstruktor? (Metode Kelas "Khusus") 5
2.2 Seluruh proses membuat objek 5
2.3 Penggunaan alternatif konstruktor (menggunakan referensi kelas untuk mengimplementasikan polimorfisme konstruktor) 6
3 Objek Destructor and Destruction 7
3.1 Apa itu destruktor (metode virtual "alami") 7
3.2 Seluruh proses penghancuran objek 7
3.3 Hancurkan, Gratis, Freeandnil, Penggunaan Rilis dan Perbedaan 7
4 VCL Construction & Destructurturing Architecture 8
5 Gunakan dengan benar konstruktor dan destruktor 9
Analisis Struktur dan Penghancuran di Delphi
Abstrak: Melalui studi VCL/RTL, makalah ini menganalisis mekanisme implementasi konstruktor dan destruktor dan arsitektur objek di VCL, dan menjelaskan cara membuat dan melepaskan objek dengan benar.
Kata kunci: membangun, menghancurkan, membuat objek, menghancurkan objek, tumpukan, tumpukan, polimorfisme.
Penulis: Majorsoft
pertanyaan
Apa mekanisme implementasi konstruktor dan destruktor di Delphi? Bagaimana cara membuat dan melepaskan objek dengan benar?
Larutan
Cara menggunakan konstruksi dan perusak dengan benar adalah masalah umum yang kami temui dalam proses menggunakan Delphi. Berikut ini adalah untuk memahami mekanisme implementasi konstruktor dan destruktor melalui studi kode sumber VCL/RTL.
1 Model Objek di Delphi:
1.1 Apa arti nama objek?
Tidak seperti C ++, nama objek (juga disebut variabel) di Delphi mewakili referensi suatu objek, dan tidak mewakili objek itu sendiri, yang setara dengan pointer ke objek, yang disebut "Model Referensi Objek". Seperti yang ditunjukkan pada gambar:
Obj (nama objek) objek aktual
Alamat entri VMT
Anggota data
Gambar 1 Nama objek mengacu pada objek dalam memori
1.2 Di mana objek disimpan?
Setiap aplikasi membagi memori yang dialokasikan untuk bertemu ke dalam empat area:
Area Kode
Area Data Global
Area tumpukan
Area tumpukan
GAMBAR 2 Ruang memori program
Area Kode: Simpan Kode Program Dalam Program, termasuk semua kode fungsi
Area Data Global: Menyimpan Data Global.
Area tumpukan: Juga dikenal sebagai "area penyimpanan gratis", yang menyimpan data dinamis (termasuk objek dan string di Delphi). Lingkup adalah seluruh siklus hidup aplikasi sampai destruktor dipanggil.
Area Stack: Juga dikenal sebagai "Area Penyimpanan Otomatis" untuk menyimpan data lokal dalam suatu program. Lingkup berada di dalam fungsi, dan sistem segera mendaur ulang ruang tumpukan setelah fungsi dipanggil.
Dalam C ++, objek dapat dibuat pada tumpukan, atau pada tumpukan, atau dalam data global. Di Delphi, semua objek dibangun di atas area penyimpanan tumpukan, sehingga konstruktor Delphi tidak dapat dipanggil secara otomatis, tetapi harus dipanggil oleh programmer itu sendiri (seret komponen dalam perancang, dan objek dibuat oleh Delphi). Program berikut menjelaskan perbedaan antara membuat objek di Delphi dan C ++:
Di Delphi:
Prosedur CreateObject (var fooobjref: tfooObject);
Mulai
Fooobjref: = tfooObject.create;
// Dipanggil oleh programmer, setelah prosedur dipanggil, objek masih ada.
Fooobject.caption = 'Saya dibuat di stack of createObject ()';
Akhir;
Dan di C ++:
TfooObject createObject (void);
{
Tfooobject fooobject; // buat objek lokal
// static tfooobject fooobject; // buat objek lokal statis
// Objek secara otomatis dibuat dengan memanggil konstruktor default, dan objek dibuat di tumpukan fungsi saat ini.
Fooobject.caption = 'Saya dibuat di stack of createObject ()';
Kembalikan FooObject;
// Objek disalin saat kembali, dan objek yang dibuat asli akan dihancurkan secara otomatis setelah panggilan fungsi selesai}
TfooObject fooobject2; // Buat objek global.
void main ();
{TfooObject* pfooObject = tfooObject baru;
// Buat objek tumpukan. Setelah fungsi dipanggil, objek masih ada dan tidak perlu disalin. }
1.3 Apa yang disimpan di objek? Bagaimana mereka disimpan?
Tidak seperti C ++, objek di Delphi hanya menyimpan alamat entri anggota data dan tabel metode virtual (VMT), tetapi tidak menyimpan metode, seperti yang ditunjukkan pada gambar:
Objek Metode Virtual Segmen Kode Tabel
Alamat VMT
Nama: String
Lebar: Integer;
CH1: char;
...
Proc1
Func1
...
Procn
funcn
...
Gambar 3 Struktur objek ...
Mungkin Anda memiliki beberapa pertanyaan tentang pernyataan di atas, silakan lihat prosedur berikut:
TsizeAlignTest = kelas
Pribadi
I: Integer;
CH1, CH2: Char;
J: Integer;
publik
prosedur showmsg;
Prosedur Virtmtd;
akhir;
memo1.lines.add (inttoStr (sizetest.instancesize)+': instancesize');
memo1.lines.add (inttoStr (integer (sizetest))+'<-mulai addr');
memo1.lines.add (inttoStr (integer (@(sizetest.i)))+'<-sizetest.i');
memo1.lines.add (inttoStr (integer (@(sizetest.ch1)))+'<-sizetest.ch1');
memo1.lines.add (inttoStr (integer (@(sizetest.ch2)))+'<-sizetest.ch2');
memo1.lines.add (inttoStr (integer (@(sizetest.j)))+'<-sizetest.j');
Hasilnya menunjukkan:
16: Instancesize
14630724 <-GADR ADDR
14630728 <-sizetest.i
14630732 <-sizetest.ch1
14630733 <-sizetest.ch2
14630736 <-sizetest.j
Anggota data dan alamat entri VMT menempati 16 byte!
Jadi di mana fungsi anggota disimpan? Karena Delphi didasarkan pada RTL (pustaka jenis runtime), semua fungsi anggota disimpan di kelas. Jadi bagaimana menemukan alamat entri fungsi anggota? Untuk fungsi statis, pekerjaan ini dilakukan oleh kompiler. kali ini). Harus ada saat ini.
Melihat
Seperti disebutkan di atas, semua fungsi anggota disimpan di kelas, dan sebenarnya menyertakan tabel metode virtual VMT. Dari kode Delphi Function AutoComplete (tergantung pada informasi kompilasi), kita dapat melihat bahwa setelah kita memasukkan nama objek dan kemudian masuk ".", Delphi mengkompilasi ulang, mendaftarkan semua anggota data dan semua metode statis, semua metode virtual, semua kelas kelas Metode, semua konstruktor dan destruktor, Anda dapat mencobanya jika ini masalahnya.
Alamat entri VMT Tabel Metode Virtual Kelas
Informasi Template Anggota Data
Tabel metode statis, dll.
Tabel Metode Virtual VMT
Obyek
Alamat entri VMT
Anggota data
Program di atas juga menunjukkan penyelarasan anggota data objek (struktur data fisik), yang disejajarkan dalam 4 byte (penyelarasan default windows), seperti yang ditunjukkan pada gambar di bawah ini:
VMT Pintu Masuk Addr
Saya
CH1CH2
J
2 Konstruktor dan Membuat Objek
2.1 Apa itu konstruktor? (Metode "Khusus")
Dari semantik ide OO (berorientasi objek), konstruktor bertanggung jawab atas penciptaan objek, tetapi dalam hal implementasi bahasa OOP, apakah Delphi atau C ++, konstruktor hanya melakukan inisialisasi objek (termasuk Memanggil sub-objek internal).
Selain itu, tidak seperti di C ++, Delphi mendefinisikan tipe metode lain untuk konstruktor (mkconstructor, lihat line /source/rtl/common/typinfo.pas, baris 125 di direktori instalasi Delphi), yang dapat kami pahami sebagai metode kelas "khusus" " . Itu hanya dapat dipanggil melalui kelas (nama kelas/referensi kelas/penunjuk kelas), sementara metode kelas umum dapat dipanggil melalui kedua kelas dan objek; , dan dalam metode kelas, diri menunjuk ke kelas, di mana kami biasanya menginisialisasi anggota datanya untuk menjadikannya objek nyata, yang semuanya berkat parameter diri.
Secara default, konstruktor adalah fungsi statis. Membuat banyak konstruktor, dan juga dapat secara langsung menutupi konstruktor kelas induk di kelas turunan. Struktur & Destrukturisasi (lihat 4)
2.2 Seluruh proses pembuatan objek
Proses lengkap membuat objek harus mencakup mengalokasikan ruang, membangun struktur data fisik, menginisialisasi, dan membuat sub-objek internal. Seperti disebutkan di atas, konstruktor hanya bertanggung jawab untuk inisialisasi dan memanggil konstruktor sub-objek internal. Ini karena kompiler melakukan hal -hal ekstra, kami tidak tahu. Saat menyusun konstruktor, sebelum membangun fungsi, baris "Call @ClassCreate" kode akan dimasukkan. :
function _classCreate (aclass: tclass; alloc: boolean): tobject;
Asm
{ -> eax = pointer ke vmt}
{<- eax = pointer to instance}
...
Hubungi DWORD PTR [EAX] .VMTNewInstance // Memanggil NewInstance
...
Akhir;
VMTNewInstance = -12; Ini adalah offset dari fungsi newinstance di kelas, lalu panggil DWORD PTR [EAX] .VMTNewInstance "sebenarnya memanggil newinstance.
Fungsi Kelas NewInstance: Tobject;
fungsi kelas tobject.newinstance: tobject;
Mulai
Hasil: = initInstance (_getMem (InSstancesize));
akhir;
"InitInstance (_getMem (Insstancesize))" memanggil tiga fungsi secara bergantian:
1) Instancesize panggilan pertama () untuk mengembalikan ukuran objek dari kelas yang sebenarnya
Fungsi kelas Tobject.instancesize: Longint; // setara dengan metode virtual
Mulai
Hasil: = pinteger (integer (self) + vmtinstancesize)^; // mengembalikan ukuran objek dari kelas aktual
akhir;
2) Hubungi _getMem () untuk mengalokasikan memori berukuran instance pada tumpukan dan mengembalikan referensi objek
3) Call InitInstance () untuk membangun struktur data fisik dan mengatur nilai default anggota, seperti mengatur nilai anggota data integer ke 0, mengatur pointer ke nihil, dll. Jika ada metode virtual, tetapkan alamat entri tabel metode virtual VMT ke empat byte pertama objek.
Setelah memanggil newinstance, objek saat ini hanya memiliki "shell kosong" dan tidak ada "konten" yang sebenarnya, jadi perlu untuk memanggil konstruktor yang disesuaikan untuk menginisialisasi objek secara bermakna, dan memanggil konstruktor sub-objek internal, aktifkan Objek dalam program untuk benar -benar mencerminkan objek di dunia nyata. Ini adalah seluruh proses pembuatan objek.
2.3 Penggunaan alternatif konstruktor (menggunakan referensi kelas untuk mengimplementasikan polimorfisme konstruktor)
Di Delphi, kelas juga disimpan sebagai objek, sehingga ada juga polimorfisme. Atur metode kelas sebagai metode virtual, angkanya di kelas turunannya, dan kemudian panggil melalui referensi/penunjuk kelas dasar, sehingga objek dibangun berdasarkan referensi kelas/penunjuk yang menunjuk ke kelas aktual. Silakan lihat program berikut:
Tmyclass = kelas
konstruktor membuat; virtual;
akhir;
Ttmyclass = kelas tmyclass; // Referensi kelas kelas dasar
Tmyclasssub = kelas (tmyclass)
konstruktor membuat;
akhir;
prosedur createObj (aclass: ttmyclass; var ref);
Mulai
Tobject (Ref): = aclass.create;
// Ref tidak ada jenis dan tidak kompatibel dengan jenis apa pun, jadi harus secara eksplisit dilemparkan saat digunakan (cast)
// AClass adalah referensi kelas, antarmuka fungsi terpadu, dan implementasi yang berbeda.
// Ini akan membuat objek berdasarkan kelas aktual yang dirujuk/ditunjuk oleh Aclass.
Akhir;
...
Createobj (tmyclass, obj);
Createobj (tmyclasssub, subobj);
3 Destructor dan Hancurkan Benda
3.1 Apa itu penghancur (metode virtual "secara alami")
Secara semantik, destruktor bertanggung jawab untuk menghancurkan objek dan melepaskan sumber daya. Di Delphi, identik.
Delphi juga mendefinisikan jenis metode untuk destruktor (MKConstructor, lihat line /source/rtl/common/typinfo.pas, 125 di direktori instalasi Delphi). Di VCL, ini adalah metode virtual "alami", mendefinisikan "destructor destructor). ; Mengapa VCL melakukan ini? Karena memastikan bahwa objek dapat dirusak dengan benar dalam situasi polimorfik. Jika metode virtual tidak digunakan, Anda hanya dapat menghancurkan subobject kelas dasar, menghasilkan apa yang disebut "kebocoran memori". Oleh karena itu, untuk memastikan destruktor yang benar, destruktor perlu menambahkan deklarasi override.
3.2 Seluruh proses penghancuran objek
Pertama -tama hancurkan subobject kelas yang diturunkan, dan kemudian hancurkan subobject kelas dasar.
petunjuk
Dalam kelas yang diturunkan, sub-objek kelas dasar mengacu pada bagian yang diwarisi dari kelas dasar, dan objek anak di kelas yang diturunkan mengacu pada bagian yang baru ditambahkan.
3.3 Hancurkan, Gratis, FreeAndnil, Penggunaan Rilis dan Perbedaan
Hancurkan: Metode virtual
Lepaskan memori, nyatakannya sebagai virtual dalam Tobject, biasanya mengganti subkelasnya, dan menambahkan kata kunci yang diwariskan untuk memastikan bahwa objek kelas yang diturunkan dihancurkan dengan benar;
Tapi hancurkan tidak dapat digunakan secara langsung, mengapa?
Jika suatu objek nihil, kami masih memanggil hancur, kesalahan akan terjadi. Karena Destroy adalah metode virtual, ia perlu menemukan alamat pintu masuk Tabel Metode Virtual VMT berdasarkan empat byte pertama dalam objek, sehingga menemukan alamat masuk kehancuran, sehingga objek harus ada saat ini. Tetapi gratis adalah metode statis. Menggunakan gratis lebih aman daripada menggunakan Destroy.
2) GRATIS: Metode statis
Uji apakah objek itu nihil, dan hancurkan dipanggil jika bukan nol. Ini kode Delphi secara gratis:
Prosedur Tobject.Free;
Mulai
Jika self <> nil maka
Menghancurkan;
akhir;
Bukankah bagus untuk belajar dari kekuatan seseorang dan kelemahan seseorang?
Namun, Calling Destroy hanya menghancurkan objek, tetapi tidak menetapkan referensi objek ke NIL, yang perlu dilakukan oleh programmer.
3) FreeAndnil;
Definisi freeandnil di unit sysutils
Prosedur freeandnil (var obj);
var
Temp: Tobject;
Mulai
Temp: = Tobject (obj);
Pointer (OBJ): = nil;
Temp.Free;
akhir;
Kami menyarankan Anda menggunakannya alih -alih gratis/hancur untuk memastikan bahwa objek dilepaskan dengan benar.
4) Rilis;
Fungsi bebas dipanggil setelah semua peristiwa di jendela diproses. Ini sering digunakan untuk menghancurkan jendela, dan ketika pemrosesan acara membutuhkan waktu tertentu di jendela ini, metode ini dapat memastikan bahwa jendela dihancurkan hanya setelah acara jendela diproses. Berikut adalah kode sumber Delphi dari tcustomform.release:
prosedur tcustomform.release;
Mulai
Postmessage (pegangan, cm_release, 0, 0);
// Kirim pesan CM_RELEASE ke jendela ke antrian pesan.
// hubungi proses pemrosesan pesan CM_RELEASE CMRelease lagi
akhir;
Mari kita lihat definisi CMRelease berikut untuk pemrosesan pesan CM_RELEASE:
Prosedur CMRelease (Pesan VAR: TMessage);
Prosedur tcustomform.cmrelease;
Mulai
Gratis;
akhir;
4 VCL Construction & Destructurturing Architecture
Tobject
konstruktor membuat; // metode statis
Destructor menghancurkan;
Tpersistent
Destructor menghancurkan;
Tcomponent
Konstruktor Create (Aowner: TComponent);
Destructor menghancurkan;
Tconstrol
Constructor Create (Aowner: TComponent);
Destructor menghancurkan;
...
Berikut ini menganalisis kode sumber konstruksi dan perusak di VCL, mengambil tcontrol sebagai contoh:
konstruktor tconstrol.create (Aowner: tComponent);
Mulai
Diwariskan Create (Aowner); // Buat Subobject Kelas Dasar dan Serahkan Hak Menghancurkan kepada Aowner. Letakkan di depan
// Ini memastikan urutan "Membuat Subobject Kelas Dasar Pertama, lalu Membuat Subobjects Kelas Turunan"
… // inisialisasi, dan hubungi konstruktor subobject internal
akhir;
Destructor tconstrol.destroy;
Mulai
… // Destruct Internal Subobjects di kelas turunan
diwariskan hancur; // menghancurkan objek kelas dasar dan meletakkannya di akhir
// Ini memastikan urutan "penghancuran pertama subobject kelas turunan, kemudian penghancuran subobjects kelas dasar"
akhir;
5 Gunakan konstruktor dan destruktor dengan benar
Setelah analisis di atas, berikut ini merangkum prinsip -prinsip menggunakan konstruktor dan destruktor:
Sebelum menggunakan objek, Anda harus terlebih dahulu membuat objek dan menghancurkan objek tepat waktu untuk membebaskan sumber daya.
Ketika dua objek merujuk pada penugasan, pastikan bahwa objek tanpa nama (mengacu pada objek yang tidak dirujuk) yang muncul dapat dilepaskan.
Saat membuat komponen, disarankan untuk mengatur komponen host (yaitu, gunakan parameter aowner, biasanya formulir), dan Aowner mengelola penghancuran objek, jadi tidak perlu khawatir tentang menghancurkan komponen ini Delphi pada desain modul form/data dan membuat komponen adalah metode yang diambil. Jadi kita tidak perlu menulis destruktor yang memanggil komponen.
Ketika tipe pengembalian fungsi adalah suatu objek, hasilnya juga merupakan referensi ke objek, memastikan bahwa objek yang dirujuk oleh hasil harus ada.
Untuk menggunakan obj <> nil atau ditugaskan (nil) untuk menguji bahwa objek ada, obj: = nil juga harus dipanggil setelah obj: = nil.
Silakan merujuk ke kode sumber program demo
Instruksi (disarankan)
Semua program Delphi telah diteruskan pada Win2K+Delphi6 SP2. Untuk memperdalam pemahaman Anda tentang artikel ini, disarankan untuk merujuk pada program demonstrasi.
Artikel ini mencakup beberapa pengalaman dan pengalaman saya dalam belajar VCL/RTL.
Sebelum membaca artikel ini, pembaca perlu memiliki pemahaman tertentu tentang bahasa Pascal yang berorientasi dan dapat memahami polimorfisme.
Melalui artikel ini, Anda harus dapat memahami model objek, mekanisme implementasi konstruksi & perusak di Delphi, dan arsitektur konstruksi & destruktur di VCL, dan dapat menguasai penggunaan metode konstruksi & perusak. Struktur & kehancuran di Delphi jauh lebih sederhana daripada di C ++, dan kita harus dapat menguasainya.