Aracılığıyla paylaş


Değer Türleri Anlamları

Değer türü mantıkları C++ için Yönetilen uzantılardan Visual C++ 2010'a değişmiştir.

Aşağıda C++ için Yönetilen Uzantılar belirtiminde kullanılan kurallı basit değer türleri vardır:

__value struct V { int i; };
__gc struct R { V vr; };

Yönetilen Uzantılarda, değer türünün söz dizimine göre dört farklı türevine (biçim 2 ve 3 mantıksal olarak aynıdır) sahip olabiliriz:

V v = { 0 };       // Form (1)
V *pv = 0;         // Form (2) an implicit form of (3)
V __gc *pvgc = 0;  // Form (3)
__box V* pvbx = 0; // Form (4) must be local 

Devralınan Sanal Yöntemleri Çağırma

Form (1) kurallı değer nesnesidir ve birinin ToString() gibi bir devralınan sanal işlevi çağırmayı denemesi dışında olabildiğince iyi anlaşılmıştır. Örneğin:

v.ToString(); // error!

Bu yöntemi çağırmak için, V'de geçersiz kılınmadığından, derleyicinin ilgili temel sınıf sanal tablosuna erişimi olmalıdır. Değer türleri, kendi sanal tablolarıyla ilişkili işaretçileri (vptr) olmayan durumlu depolama birimi olduğundan, bu v'nin kutulanmış olmasını gerektirir. Yönetilen Uzantılar dil tasarımında, dolaylı olarak kutulama desteklenmemiştir ancak programlayıcı tarafından açıkça belirtilmelidir, aşağıdaki gibi

__box( v )->ToString(); // Managed Extensions: note the arrow

Bu tasarımın arkasındaki birincil neden pedagojik: altta yatan mekanizmanın programcıya görünür olması gerekir böylelikle kendi değer türü içinde örnek sağlamamasının nelere mal olacağını anlayabilir. V ToString örneği barındırdığında, kutulama gereksiz olacaktır.

Nesneyi açıkça kutulamanın sözcük temelli karmaşıklığı yeni söz diziminde kaldırıldı, ancak kutulamanın kendi temel maliyeti kaldırılmadı:

v.ToString(); // new syntax

ancak V içinde açık ToString yöntem örneği sağlamamanın maliyetine karşılık sınıf tasarımcısını muhtemel yanlış yönlendirme maliyetiyle. Dolaylı kutulamayı tercihin sebebi, genellikle sadece bir sınıf tasarımcısı varken muhtemel sıkıntılı açık kutulamayı kaldırmak için V'yi değiştirme özgürlüğü bulunmayan sınırsız sayıda kullanıcı vardır.

Değer sınıfında ToString'i geçersiz kılan örnekler sağlayıp sağlamamanın kriteri bunun kullanım yeri ve sıklığı olmalıdır. Eğer çok nadiren çağrılıyorsa, tabiki tanımında az kazanç vardır. Benzer şekilde, eğer uygulamanın etkin olmayan bölgelerinde çağrılıyorsa, bunu eklemek uygulamanın genel başarımına fazla bir şey eklemez. Alternatif olarak, biri kutulanmış değere izleme işleyicisi tutabilir ve bu işleyici üzerinden olan çağrılar kutulama gerektirmez.

Artık Değer Sınıfı Varsayılan Yapıcısı Yoktur

Yönetilen Uzantılar ve yeni söz dizimi arasındaki diğer bir değer türü farkı varsayılan yapıcı desteğinin kaldırılmasıdır. Bu yürütme sırasında CLR'nin ilgili varsayılan yapıcıyı çağırmadan değer türü örneği oluşturabildiği durumlar olmasındandır. Bir başka değişle, Yönetilen Uzantılar altında değer türünde varsayılan yapıcı destekleme denemesi pratikte garanti edilmez. Bu garanti yokluğu verildiğinde, uygulamada kararlı olmayan şekilde bulundurmaktansa desteği tamamen kaldırmanın daha iyi olduğu düşünülmüştür.

Bu başlangıçta göründüğü kadar kötü değildir. Bu her değer türü nesnesinin otomatik olarak sıfırlanmasındandır (diğer bir değişle, her tür kendi varsayılan değeri ile başlatılır). Sonuç olarak, yerel örneğin üyeleri hiçbir zaman tanımsız değildir. Bu anlamda, basit varsayılan yapıcı tanımlama özelliği kaybı aslında bir kayıp değildir ve aslında CLR tarafından gerçekleştirildiğinde daha verimlidir.

Sorun, Yönetilen Uzantılar kullanıcısı basit olmayan varsayılan yapıcı tanımladığındadır. Bunun yeni söz diziminde eşlemesi yoktur. Yapıcıdaki kodun daha sonra kullanıcı tarafından açıkça çağrılması gereken isimli başlangıç yöntemine geçirilmesi gerekecektir.

Yeni söz diziminde değer türü nesnesi bildirimi diğer durumlarda değişmemiştir. Bunun olumsuz tarafı aşağıdaki nedenlerden dolayı değer türlerinin yerel türleri sarmalamada yetersiz olmasıdır:

  • Değer türünde yıkıcı desteği yoktur. Diğer bir değişle, nesnenin ömrünün bitişiyle tetiklenen eylemler kümesini otomatikleştirmenin bir yolu yoktur.

  • Yerel sınıf sadece yönetilen tür içerisinde sonra yerel yığında ayrılacak olan bir işaretçi olarak bulundurulur.

Bir çift yığın ayrılmasını engellemek için küçük bir yerel sınıfı bir başvuru türündense bir değer türüne sarmalamayı isteriz: yerel yığın yerel türü, CLR yığın yönetilen sarmalayıcıyı tutar. Yerel sınıfı değer türünde sarmalamak yönetilen yığını engellemenize izin verir, ancak yerel yığın belleğinin yeniden kullanılabilir duruma gelmesini otomatikleştirmenin yolu yoktur. Basit olmayan yerel sınıfları sarmalamak için başvuru türleri tek kullanışlı yönetilen türlerdir.

İç İşaretçiler

Yukarıdaki Form (2) ve Form (3) bu dünyadaki ve sonrakindeki (diğer bir değişle, yönetilen veya yerel her şey) neredeyse her şeyi adresleyebilir. Böylece, örneğin, aşağıdakilerin hepsine Yönetilen Uzantılarda izin verilmiştir:

__value struct V { int i; };
__gc struct R { V vr; };

V v = { 0 };  // Form (1)
V *pv = 0;  // Form (2)
V __gc *pvgc = 0;  // Form (3)
__box V* pvbx = 0;  // Form (4)

R* r;

pv = &v;            // address a value type on the stack
pv = __nogc new V;  // address a value type on native heap
pv = pvgc;          // we are not sure what this addresses
pv = pvbx;          // address a boxed value type on managed heap
pv = &r->vr;        // an interior pointer to value type within a
                    //    reference type on the managed heap

Böylece, V* yerel blokta (ve bu sebeple sarkan olabilir), genel kapsamda, yerel yığında (örneğin, adreslediği nesne zaten silinmişse), CLR yığınında (ve bu sebeple atık toplama sırasında yeri değişmesi gerekirse diye izlenecektir), CLR yığınındaki başvuru nesnesinin içinde (iç işaretçi, çağrıldığı gibi saydam şekilde izlenir) bir yeri adresleyebilir.

Yönetilen Uzantılarda, V*'nin yerel yönlerini ayırmanın bir yolu yoktur; diğer bir değişle, yönetilen yığında bir nesneyi veya alt nesneyi adresleme ihtimalinin işlendiği kendi içinde değerlendirilir.

Yeni söz diziminde değer türü işaretçi iki türe ayrılmıştır: CLR olmayan yığın konumlarına sınırlı V* ve yönetilen yığında adrese izin veren ancak gerektirmeyen interior_ptr<V> iç işaretçisi.

// may not address within managed heap 
V *pv = 0; 

// may or may not address within managed heap
interior_ptr<V> pvgc = nullptr; 

Yönetilen Uzantıların Form (2)'si ve Form (3)'ü interior_ptr<V>'a eşlenir. Form (4) izleme işleyicisidir. Yönetilen yığında kutulanan bütün nesneyi adresler. Yeni sözdiziminde V^ olarak çevrilmiştir.

V^ pvbx = nullptr; // __box V* pvbx = 0;  

Yönetilen Uzantılarda bulunan aşağıdaki bildirimlerin hepsi yeni sözdiziminde iç işaretçilere eşlenir. (System isim uzayındaki değer türleridir.)

Int32 *pi;   // => interior_ptr<Int32> pi;
Boolean *pb; // => interior_ptr<Boolean> pb;
E *pe;       // => interior_ptr<E> pe; // Enumeration

Yerleşik türler System isim uzayındaki türlere eş adlar olarak hizmet etmelerine rağmen yönetilen türler olarak düşünülmezler. Bu sebeple aşağıdaki eşlemeler Yönetilen Uzantılar ve yeni söz dizimi arasında doğru kalır:

int * pi;     // => int* pi;
int __gc * pi2; // => interior_ptr<int> pi2;

Var olan programınızdaki V*'yi çevirirken, en koruyucu strateji her zaman interior_ptr<V>'ye çevirmektir. Bu Yönetilen Uzantılar altındaki değerlendirilme şeklidir. Yeni sözdiziminde, programcının iç işaretçi yerine V* belirterek değer türlerini yönetilmeyen yığın adreslerine kısıtlama seçeneği vardır. Programınızı çevirirken, tüm kullanımlarını geçişli kapatma yapabilirseniz ve atanan bütün adreslerin yönetilen yığında olmadığından emin olabilirseniz, V* olarak bırakmanın bir sakıncası yoktur.

Sabitleme İşaretçileri

Atık toplayıcı isteğe bağlı olarak CLR yığınında bulunan nesneleri genellikle sıkıştırma aşaması sırasında yığında farklı yerlere taşıyabilir. Bu varlıkları saydam olarak güncelleyen izleme işleyicileri, izleme başvuruları ve iç işaretçiler için bu hareket sorun değildir. Ancak, eğer kullanıcı CLR yığınındaki nesnenin adresini çalışma zamanı ortamının dışına geçirdiyse, bu hareket sorun olur. Bu durumda, nesnelerin geçici hareketleri büyük ihtimalle çalışma zamanı hatasına sebep olur. Bu gibi nesneleri hareketten muaf tutarken, dış kullanım kapsamları için yerel olarak yerlerini sabitlemeliyiz.

Yönetilen Uzantılarda, sabitleme işaretçisi bir işaretçi bildirimi __pin anahtar sözcüğü ile yükseltilerek bildirilir. Aşağıda Yönetilen Uzantılar belirtiminden biraz değiştirilmiş bir örnek vardır:

__gc struct H { int j; };

int main() 
{
   H * h = new H;
   int __pin * k = & h -> j;
  
   // …
};

Yeni dil tasarımında, sabitleme işaretçisi o iç işaretçiye benzer bir sözdizimi ile bildirilir.

ref struct H
{
public:
   int j;
};

int main()
{
   H^ h = gcnew H;
   pin_ptr<int> k = &h->j;

   // …
}

Yeni söz dizimi altındaki sabitleme işaretçisi, iç işaretçinin özel durumudur. Sabitleme işaretçisindeki özgün kısıtlamalar kalır. Örneğin, parametre veya yöntemin dönüş türü olarak kullanılamaz; sadece yerel nesnede bildirilebilir. Ancak yeni sözdiziminde bir kaç ek kısıtlama eklenmiştir.

Sabitleme işaretçisinin varsayılan değeri 0 değil nullptr'dir. pin_ptr<> 0 olarak başlatılamaz veya atanamaz. Var olan koddaki bütün 0 atamalarının nullptr'a değişmesi gerekecektir.

Yönetilen Uzantılar altındaki sabitleme işaretçisinin tüm bir nesneyi adreslemesine izin verilmişti, Yönetilen Uzantlar bildiriminden alınan aşağıdaki örnekteki gibi:

__gc class G {
public:
   void incr(int* pi) { pi += 1; }
};
__gc struct H { int j; };
void f( G * g ) {
   H __pin * pH = new H;   
   g->incr(& pH -> j);   
};

Yeni söz diziminde, new ifadesi tarafından döndürülen tüm nesneyi sabitleme desteklenmemiştir. Bunun yerine, iç üyelerin adresleri sabitlenmelidir. Örnek:

ref class G {
public:
   void incr(int* pi) { *pi += 1; }
};
ref struct H { int j; };
void f( G^ g ) {
   H ^ph = gcnew H;
   Console::WriteLine(ph->j);
   pin_ptr<int> pj = &ph->j;
   g->incr(  pj );
   Console::WriteLine(ph->j);
}

Ayrıca bkz.

Başvuru

Classes and Structs (Managed)

interior_ptr

pin_ptr

Kavramlar

Değer Türleri ve Davranışları