Jawaban:
Ini nilai referensi abstrak ke sumber daya, sering memori atau file terbuka, atau pipa.
Benar , di Windows, (dan umumnya dalam komputasi) pegangan adalah abstraksi yang menyembunyikan alamat memori nyata dari pengguna API, yang memungkinkan sistem untuk mengatur kembali memori fisik secara transparan ke program. Memecahkan pegangan ke penunjuk mengunci memori, dan melepaskan pegangan membatalkan penunjuk. Dalam hal ini anggap itu sebagai indeks ke dalam tabel petunjuk ... Anda menggunakan indeks untuk panggilan sistem API, dan sistem dapat mengubah pointer di tabel sesuka hati.
Atau, penunjuk nyata dapat diberikan sebagai pegangan ketika penulis API bermaksud agar pengguna API diisolasi dari spesifikasi alamat yang dikembalikan; dalam hal ini harus dipertimbangkan bahwa apa yang ditunjukkan oleh pegangan dapat berubah kapan saja (dari versi API ke versi atau bahkan dari panggilan ke panggilan API yang mengembalikan gagang) - karena itu gagang harus diperlakukan hanya sebagai nilai buram hanya berarti bagi API.
Saya harus menambahkan bahwa dalam sistem operasi modern apa pun, bahkan apa yang disebut "pointer nyata" masih merupakan pegangan buram ke dalam ruang memori virtual dari proses, yang memungkinkan O / S untuk mengelola dan mengatur ulang memori tanpa membatalkan pointer dalam proses. .
A HANDLE
adalah pengidentifikasi unik khusus konteks. Secara spesifik konteks, maksud saya bahwa pegangan yang diperoleh dari satu konteks tidak selalu dapat digunakan dalam konteks aribtrary lain yang juga berfungsi pada HANDLE
s.
Misalnya, GetModuleHandle
mengembalikan pengidentifikasi unik ke modul yang saat ini dimuat. Pegangan yang dikembalikan dapat digunakan dalam fungsi lain yang menerima pegangan modul. Itu tidak dapat diberikan ke fungsi yang membutuhkan jenis pegangan lainnya. Misalnya, Anda tidak dapat memberikan pegangan kembali dari GetModuleHandle
ke HeapDestroy
dan mengharapkannya melakukan sesuatu yang masuk akal.
Itu HANDLE
sendiri hanyalah tipe integral. Biasanya, tetapi tidak harus, itu adalah penunjuk ke beberapa tipe atau lokasi memori yang mendasarinya. Sebagai contoh, yang HANDLE
dikembalikan oleh GetModuleHandle
sebenarnya adalah pointer ke alamat memori virtual dasar modul. Tetapi tidak ada aturan yang menyatakan bahwa pegangan harus pointer. Pegangan juga bisa berupa integer sederhana (yang mungkin dapat digunakan oleh beberapa Win32 API sebagai indeks ke dalam array).
HANDLE
S adalah representasi sengaja buram yang menyediakan enkapsulasi dan abstraksi dari sumber daya Win32 internal. Dengan cara ini, Win32 APIs berpotensi mengubah tipe yang mendasari di belakang HANDLE, tanpa memengaruhi kode pengguna dengan cara apa pun (setidaknya itulah idenya).
Pertimbangkan tiga implementasi internal berbeda dari Win32 API yang baru saja saya buat, dan anggap itu Widget
adalah a struct
.
Widget * GetWidget (std::string name)
{
Widget *w;
w = findWidget(name);
return w;
}
void * GetWidget (std::string name)
{
Widget *w;
w = findWidget(name);
return reinterpret_cast<void *>(w);
}
typedef void * HANDLE;
HANDLE GetWidget (std::string name)
{
Widget *w;
w = findWidget(name);
return reinterpret_cast<HANDLE>(w);
}
Contoh pertama memperlihatkan detail internal tentang API: memungkinkan kode pengguna untuk mengetahui yang GetWidget
mengembalikan pointer ke struct Widget
. Ini memiliki beberapa konsekuensi:
Widget
structWidget
struct yang dikembalikanKedua konsekuensi ini mungkin tidak diinginkan.
Contoh kedua menyembunyikan detail internal ini dari kode pengguna, dengan mengembalikan adil void *
. Kode pengguna tidak perlu akses ke tajuk yang mendefinisikanWidget
struct.
Contoh ketiga persis sama dengan yang kedua, tetapi kita sebut saja void *
a HANDLE
sebagai gantinya. Mungkin ini mencegah kode pengguna untuk mencoba mencari tahu apa sebenarnya void *
poinnya.
Mengapa harus melalui masalah ini? Pertimbangkan contoh keempat ini dari versi yang lebih baru dari API yang sama ini:
typedef void * HANDLE;
HANDLE GetWidget (std::string name)
{
NewImprovedWidget *w;
w = findImprovedWidget(name);
return reinterpret_cast<HANDLE>(w);
}
Perhatikan bahwa antarmuka fungsi identik dengan contoh ketiga di atas. Ini berarti bahwa kode pengguna dapat terus menggunakan versi baru API ini, tanpa perubahan apa pun, meskipun implementasi "di belakang layar" telah berubah untuk menggunakanNewImprovedWidget
struct sebagai gantinya.
Pegangan dalam contoh ini benar-benar hanya nama baru, mungkin lebih ramah, untuk void *
, yang persis seperti apa yang HANDLE
ada di Win32 API (lihat di MSDN ). Ini memberikan dinding buram antara kode pengguna dan representasi internal perpustakaan Win32 yang meningkatkan portabilitas, antara versi Windows, kode yang menggunakan Win32 API.
handle
alih-alih void *
adalah mencegah kode pengguna untuk mencoba mencari tahu persis apa yang ditunjukkan oleh void * . Apakah saya benar?
HANDLE dalam pemrograman Win32 adalah token yang mewakili sumber daya yang dikelola oleh kernel Windows. Pegangan bisa ke jendela, file, dll.
Pegangan hanyalah cara mengidentifikasi sumber daya partikulat yang ingin Anda gunakan dengan menggunakan Win32 API.
Jadi misalnya, jika Anda ingin membuat Window, dan menunjukkannya di layar Anda bisa melakukan hal berikut:
// Create the window
HWND hwnd = CreateWindow(...);
if (!hwnd)
return; // hwnd not created
// Show the window.
ShowWindow(hwnd, SW_SHOW);
Dalam contoh di atas HWND berarti "pegangan ke jendela".
Jika Anda terbiasa dengan bahasa berorientasi objek, Anda dapat menganggap HANDLE sebagai instance dari kelas tanpa metode yang statusnya hanya dapat dimodifikasi oleh fungsi lain. Dalam hal ini ShowWindow fungsi mengubah status dari PENANGANAN Jendela.
Lihat Pegangan dan Jenis Data untuk informasi lebih lanjut.
HANDLE
ADT dikelola oleh kernel. Jenis pegangan lain yang Anda beri nama ( HWND
, dll.) Di sisi lain, adalah objek USER. Itu tidak dikelola oleh kernel Windows.
Pegangan adalah pengidentifikasi unik untuk objek yang dikelola oleh Windows. Ini seperti sebuah pointer , tetapi bukan sebuah pointer di dalam arti bahwa itu bukan alamat yang dapat ditinjau ulang oleh kode pengguna untuk mendapatkan akses ke beberapa data. Sebaliknya pegangan harus diteruskan ke serangkaian fungsi yang dapat melakukan tindakan pada objek yang diidentifikasi pegangan.
Jadi pada level paling dasar, HANDLE dalam bentuk apa pun adalah pointer ke pointer atau
#define HANDLE void **
Sekarang mengapa Anda ingin menggunakannya
Mari kita siapkan:
class Object{
int Value;
}
class LargeObj{
char * val;
LargeObj()
{
val = malloc(2048 * 1000);
}
}
void foo(Object bar){
LargeObj lo = new LargeObj();
bar.Value++;
}
void main()
{
Object obj = new Object();
obj.val = 1;
foo(obj);
printf("%d", obj.val);
}
Jadi karena obj diteruskan oleh nilai (membuat salinan dan memberikannya ke fungsi) untuk foo, printf akan mencetak nilai asli 1.
Sekarang jika kami memperbarui foo ke:
void foo(Object * bar)
{
LargeObj lo = new LargeObj();
bar->val++;
}
Ada kemungkinan bahwa printf akan mencetak nilai yang diperbarui dari 2. Tetapi ada juga kemungkinan bahwa foo akan menyebabkan beberapa bentuk kerusakan atau pengecualian memori.
Alasannya adalah ini sementara Anda sekarang menggunakan pointer untuk meneruskan obj ke fungsi Anda juga mengalokasikan 2 Megs memori, ini dapat menyebabkan OS untuk memindahkan memori sekitar memperbarui lokasi obj. Karena Anda telah melewati pointer dengan nilai, jika obj dipindahkan maka OS memperbarui pointer tetapi tidak menyalin dalam fungsi dan berpotensi menyebabkan masalah.
Pembaruan terakhir untuk:
void foo(Object **bar){
LargeObj lo = LargeObj();
Object * b = &bar;
b->val++;
}
Ini akan selalu mencetak nilai yang diperbarui.
Lihat, ketika kompiler mengalokasikan memori untuk pointer itu menandainya sebagai tidak bergerak, sehingga setiap pengocokan kembali memori yang disebabkan oleh objek besar yang dialokasikan nilai yang diteruskan ke fungsi akan menunjuk ke alamat yang benar untuk mengetahui lokasi akhir dalam memori untuk memperbarui.
Setiap jenis HANDLE tertentu (hWnd, FILE, dll.) Adalah spesifik domain dan arahkan ke jenis struktur tertentu untuk melindungi dari kerusakan memori.
Pegangan seperti nilai kunci utama dari catatan dalam database.
sunting 1: baik, mengapa downvote, kunci primer secara unik mengidentifikasi catatan basis data, dan pegangan dalam sistem Windows secara unik mengidentifikasi jendela, file yang dibuka, dll. Itulah yang saya katakan.
Pikirkan jendela di Windows sebagai struktur yang menjelaskannya. Struct ini merupakan bagian internal Windows dan Anda tidak perlu tahu detailnya. Sebaliknya, Windows menyediakan typedef untuk pointer ke struct untuk struct itu. Itu adalah "pegangan" dimana Anda dapat memegang di jendela.,