Yanıtlar:
Bir kaynağa, genellikle belleğe, açık bir dosyaya veya bir boruya soyut bir referans değerdir.
Düzgün olarak , Windows'da (ve genellikle bilgi işlemde), tanıtıcı, API kullanıcısının gerçek bir bellek adresini gizleyen ve sistemin fiziksel belleği programa şeffaf bir şekilde yeniden düzenlemesine izin veren bir soyutlamadır. Bir tanıtıcıyı işaretçiye dönüştürmek belleği kilitler ve tutamacı serbest bırakmak işaretçiyi geçersiz kılar. Bu durumda bunu bir işaretçi tablosuna dizin olarak düşünün ... sistem API çağrıları için dizini kullanırsınız ve sistem tablodaki işaretçiyi istediği gibi değiştirebilir.
Alternatif olarak, API yazıcısı, API kullanıcısının adresin gösterdiği noktanın özelliklerinden yalıtılmasını istediğinde, tanıtıcı olarak gerçek bir işaretçi verilebilir; bu durumda, tutamacın herhangi bir zamanda değişebileceği dikkate alınmalıdır (API sürümünden sürüme veya hatta, tanıtıcıyı döndüren API'nin çağrısından çağrısına) - bu nedenle, tanıtıcı basit bir opak değer olarak ele alınmalıdır. sadece API için anlamlıdır .
Ben herhangi bir modern işletim sisteminde, sözde "gerçek işaretçiler" bile hala işlemin sanal bellek alanı içinde opak kolları vardır, bu O / S işlem işaretçileri geçersiz olmadan belleği yönetmek ve yeniden düzenlemek için olanak sağlar .
A HANDLE
, bağlama özgü benzersiz bir tanımlayıcıdır. Bağlama özgü olarak, bir bağlamdan elde edilen bir tutamacın, HANDLE
s üzerinde de çalışan başka herhangi bir diğer bağlamda mutlaka kullanılamayacağını kastediyorum .
Örneğin, GetModuleHandle
yüklü bir modüle benzersiz bir tanımlayıcı döndürür. Döndürülen tutamak, modül tutamaçlarını kabul eden diğer işlevlerde kullanılabilir. Diğer tutamaç türlerini gerektiren işlevlere verilemez. Örneğin, dönen bir kolu veremedim GetModuleHandle
etmek HeapDestroy
ve bir şey mantıklı şekilde davranmasını bekliyoruz.
HANDLE
Kendisi sadece ayrılmaz bir türüdür. Genellikle, ancak zorunlu olmamakla birlikte, altta yatan bazı tür veya bellek konumunun bir göstergesidir. Örneğin, HANDLE
döndürülen GetModuleHandle
, aslında modülün temel sanal bellek adresinin bir göstergesidir. Ancak, tutamaçların işaretçi olması gerektiğini belirten bir kural yoktur. Bir tanıtıcı da basit bir tamsayı olabilir (muhtemelen bazı Win32 API tarafından bir dizinin indeksi olarak kullanılabilir).
HANDLE
s, dahili Win32 kaynaklarından kapsülleme ve soyutlama sağlayan kasıtlı olarak opak gösterimlerdir. Bu şekilde, Win32 API'leri kullanıcı kodunu herhangi bir şekilde etkilemeden bir HANDLE'ın arkasındaki temel türü değiştirebilir (en azından fikir budur).
Sadece uydurduğum bir Win32 API bu üç farklı iç uygulamalarını düşünün ve farz Widget
bir olduğunu 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);
}
İlk örnek, API ile ilgili dahili ayrıntıları ortaya koyar: kullanıcı kodunun GetWidget
bir işaretçiyi bir struct Widget
. Bunun birkaç sonucu vardır:
Widget
yapıyı tanımlayan başlık dosyasına erişimi olmalıdırWidget
yapının dahili bölümlerini değiştirebilirBu sonuçların her ikisi de istenmeyen olabilir.
İkinci örnek, bu iç ayrıntıyı yalnızca döndürerek kullanıcı kodundan gizler void *
. Kullanıcı kodu, Widget
yapıyı tanımlayan başlığa erişim gerektirmez .
Üçüncü örnek, tam olarak ikinci aynıdır, ama biz sadece diyoruz void *
a HANDLE
yerine. Belki de bu, kullanıcı kodunun tam olarak neyi void *
işaret ettiğini bulmaya çalışmaktan vazgeçirir .
Neden bu sorunu yaşıyorsun? Aynı API'nın daha yeni bir sürümünün bu dördüncü örneğini düşünün:
typedef void * HANDLE;
HANDLE GetWidget (std::string name)
{
NewImprovedWidget *w;
w = findImprovedWidget(name);
return reinterpret_cast<HANDLE>(w);
}
Fonksiyonun arayüzünün yukarıdaki üçüncü örnekle aynı olduğuna dikkat edin. Bu, "sahne arkasında" uygulaması NewImprovedWidget
yapıyı kullanmak için değişmiş olsa bile, kullanıcı kodunun API'nın bu yeni sürümünü herhangi bir değişiklik yapmadan kullanmaya devam edebileceği anlamına gelir .
Bu örnekte kolları için gerçekten sadece yeni, muhtemelen dostça, isim olan void *
bir tam olarak ne olduğu, HANDLE
(bana arama Win32 API olan MSDN at ). Kullanıcı kodu ile Win32 kitaplığının iç gösterimleri arasında, Windows sürümleri arasında Win32 API'sini kullanan kodun taşınabilirliğini artıran opak bir duvar sağlar.
handle
yerine void *
ise tam olarak ne hükümsüz * işaret ettiği anlamaya çalışıyorum zorlaştırır, kullanıcı kodu . Doğrumuyum?
Win32 programlamasındaki bir HANDLE, Windows çekirdeği tarafından yönetilen bir kaynağı temsil eden bir simgedir. Tanıtıcı bir pencere, dosya vb. Olabilir.
Tanıtıcılar, Win32 API'lerini kullanarak çalışmak istediğiniz parçalı bir kaynağı tanımlamanın bir yoludur.
Örneğin, bir Pencere oluşturmak ve ekranda göstermek istiyorsanız aşağıdakileri yapabilirsiniz:
// Create the window
HWND hwnd = CreateWindow(...);
if (!hwnd)
return; // hwnd not created
// Show the window.
ShowWindow(hwnd, SW_SHOW);
Yukarıdaki örnekte HWND "pencereye bir tutamaç" anlamına gelir.
Nesneye yönelik bir dile alışkınsanız, bir HANDLE'ı, durumu yalnızca diğer işlevler tarafından değiştirilebilen yöntemsiz bir sınıf örneği olarak düşünebilirsiniz. Bu durumda, ShowWindow işlevi Pencere KOLU durumunu değiştirir.
Daha fazla bilgi için bkz. Tanıtıcılar ve Veri Türleri .
HANDLE
ADT aracılığıyla başvurulan nesneler çekirdek tarafından yönetilir. Öte yandan adlandırdığınız ( HWND
, vb.) Diğer tanıtıcı türleri USER nesneleridir. Bunlar Windows çekirdeği tarafından yönetilmez.
Tanıtıcı, Windows tarafından yönetilen bir nesne için benzersiz bir tanımlayıcıdır. Bu bir işaretçi gibidir , ancak bazı verilere erişim elde etmek için kullanıcı kodu tarafından kaldırılabilecek bir adres olmadığı için bir işaretçi değildir. Bunun yerine bir tanıtıcı, tanıtıcıyı tanımladığı nesne üzerinde eylemler gerçekleştirebilen bir işlevler kümesine aktarılır.
Yani en temel seviyede herhangi bir türden bir HANDLE bir işaretçiye veya
#define HANDLE void **
Şimdi neden kullanmak isteyeceğinize
Bir kurulum yapalım:
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);
}
Nesne foo değerine göre geçtiğinden (bir kopyasını oluştur ve işleve ver), printf 1'in orijinal değerini yazdıracaktır.
Şimdi foo'yu şu şekilde güncellersek:
void foo(Object * bar)
{
LargeObj lo = new LargeObj();
bar->val++;
}
Printf 2'nin güncellenmiş değerini basma şansı vardır. Ancak foo'nun bir tür bellek bozulmasına veya istisnasına neden olma olasılığı da vardır.
Bunun nedeni, şimdi 2 Megs bellek ayırdığınız işleve nesneyi iletmek için bir işaretçi kullanırken, işletim sisteminin nesneyi güncelleştirmek için belleği hareket ettirmesine neden olabilir. İşaretçiyi değere göre geçtiğinizden, nesne hareket ettirilirse işletim sistemi işaretçiyi güncelleştirir ancak işlevdeki kopyayı güncellemez ve muhtemelen sorunlara neden olur.
Aşağıdakilerin yapılması için son güncelleme:
void foo(Object **bar){
LargeObj lo = LargeObj();
Object * b = &bar;
b->val++;
}
Bu her zaman güncellenen değeri basacaktır.
Derleyici, işaretçiler için bellek ayırdığında, bunları taşınmaz olarak işaretler, bu nedenle, tahsis edilen büyük nesnenin neden olduğu belleğin yeniden karıştırılması, işleve iletilen değerin, bellekteki son konumu bulmak için doğru adrese işaret eder. Güncelleme.
Belirli HANDLE türleri (hWnd, FILE, vb.) Alana özgüdür ve bellek bozulmasına karşı koruma sağlamak için belirli bir yapı türüne işaret eder.
Tanıtıcı, veritabanındaki bir kaydın birincil anahtar değeri gibidir.
edit 1: iyi, neden bir birincil anahtar downvote, bir veritabanı kaydını benzersiz olarak tanımlar ve Windows sistemindeki bir tanıtıcı bir pencere, açık bir dosya, vb benzersiz bir şekilde tanımlar, Ben böyle söylüyorum.
Windows'daki pencereyi, onu tanımlayan bir yapı olarak düşünün. Bu yapı, Windows'un dahili bir parçasıdır ve ayrıntılarını bilmenize gerek yoktur. Bunun yerine Windows, bu yapı için işaretçiyi yapılandırması için bir typedef sağlar. Bu, pencerede tutunabileceğiniz "tanıtıcı" dır.,