Aracılığıyla paylaş


Yok Edici Anlamında Yapılan Değişiklikler

Sınıf yok edicileri için semantik, C++ için Yönetilen Uzantılar'dan Visual C++'a geçişte büyük ölçüde değişti.

Yönetilen Uzantılar'da, bir sınıf yıkıcısına bir başvuru sınıfının içinde izin verilir ancak bir değer sınıfının içinde izin verilmedi.Bu, yeni sözdiziminde değiştirilmemiştir.Ancak, sınıf yıkıcı semantiği değişmiştir.Bu konu bu değişimin nedenlerine odaklanmakta ve bunun varolan CLR kodunun çevirisini nasıl etkileyebileceğini anlatmaktadır.Muhtemelen iki dil sürümü arasındaki en önemli programcı düzeyinde değişikliktir.

Belli Olmayan Sonlandırma

Bir nesneyle ilişkilendirilmiş bellek çöp toplayıcısı tarafından geri alınmadan önce, ilişkili Finalize yöntemi, varsa çağrılır.Bu yöntemi bir tür süper yıkıcı olarak düşünebilirsiniz, çünkü nesnenin program yaşam süresine bağlı değildir.Buna sonlandırma adı veririz.Bir Finalize yönteminin sadece çağrılacağı zamana ilişkin zamanlama veya çağrılıp çağrılmadığı tanımsızdır.Atık toplama kararlı olmayan sonlandırma göstermekte dediğimizde bunu demek isteriz.

Belli olmayan sonlandırma dinamik bellek yönetimiyle iyi çalışır.Kullanılabilir bellek yetersiz olduğunda, çöp toplayıcı başlar.Çöpü toplanmış bir ortamda belleği boşaltmak için yıkıcılar gereksizdir.Ancak, nesne bir veritabanı bağlantısı ya da bir tür kilit gibi kritik bir kaynağa sahip olduğunda, belli olmayan sonlandırma iyi çalışmaz.Bu durumda bu kaynağı en kısa zamanda yayınlamalıyız.Yerel dünyada bu, yapıcı/yıkıcı çifti kullanılarak elde edilir.Nesnenin yaşam süresi bittiği anda, ya içinde beyan edildiği yerel blok sona erdiği zaman, ya da atılmış bir özel durum nedeniyle yığın açıldığı zaman, yok edici çalışır ve kaynak otomatik olarak serbest bırakılır.Bu yaklaşım çok iyi çalışır ve Yönetilen Uzantılar öğesi altında yokluğu çok hissedilir.

CLR tarafından sağlanan çözüm IDisposable arabiriminin Dispose yöntemini uygulamaya ilişkin bir sınıfa yöneliktir.Burada sorun Disposeöğesinin kullanıcı tarafından açık bir çağırma gerektirmesidir.Bu, hataya yatkındır.C# dili, using deyimi şeklinde mütevazı bir otomasyon şekli sunar.Yönetilen Uzantılar tasarımı hiçbir özel destek sağlamamıştır.

C++ Yönetilen Uzantılarında Yıkıcılar

Yönetilen Uzantılar'da, aşağıdaki iki adım kullanılarak başvuru sınıfı yıkıcısı gerçekleştirilir:

  1. Kullanıcı tarafından sağlanan yıkıcı kendi içinde Finalize olarak yeniden adlandırılır.Sınıfın temel sınıfı varsa (CLR Nesne Modeli'nin altında sadece tek devralmanın desteklendiğini unutmayın), derleyici, kullanıcı tarafından sağlanan kodun yürütülmesinin ardından sonlandırıcıya bir çağrı ekler.Örneğin, Yönetilen Uzantılar dil belirtiminden alınmış olan aşağıdaki basit hiyerarşiyi ele alın:
__gc class A {
public:
   ~A() { Console::WriteLine(S"in ~A"); }
};
   
__gc class B : public A {
public:
   ~B() { Console::WriteLine(S"in ~B");  }
};

Bu örnekte, iki yıkıcı da Finalize olarak yeniden adlandırılır.B'ye ait Finalize öğesinin WriteLine çağrısından sonra eklenen A'ya ait Finalize yöntemi vardır.Sonlandırma sırasında atık toplayıcının varsayılan olarak çağıracağı öğe budur.Bu iç dönüşüm aşağıdaki gibi görünmelidir:

// internal transformation of destructor under Managed Extensions
__gc class A {
public:
   void Finalize() { Console::WriteLine(S"in ~A"); }
};

__gc class B : public A {
public:
   void Finalize() { 
      Console::WriteLine(S"in ~B");
      A::Finalize(); 
   }
};
  1. İkinci adımda, derleyici sanal bir yıkıcıyı sentezler.Doğrudan veya sil ifadesinin bir uygulaması yoluyla Yönetilen Uzantılar kullanıcı programlarının çağırdığı bu yıkıcıdır.Asla çöp toplayıcı tarafından çağrılmaz.

    Bu sentezlenmiş yıkıcı içine iki deyim yerleştirilmiştir.Bunlardan biri, Finalize öğesinin daha fazla etkinleştirmesi olmadığından emin olmak için GC::SuppressFinalize öğesine yapılan çağrıdır.İkincisi, bu sınıf için kullanıcının sağladığı yıkıcıyı temsil eden Finalize öğesinin gerçek bir çağrısıdır.Bu aşağıdaki gibi görünmelidir:

__gc class A {
public:
   virtual ~A() {
      System::GC::SuppressFinalize(this);
      A::Finalize();
   }
};

__gc class B : public A {
public:
   virtual ~B() {
      System::GC::SuppressFinalize(this);
      B::Finalize();
   }
};

Bu uygulama kullanıcının sınıf Finalize yöntemini, hiçbir kontrole sahip olmadığınız bir zamandansa şimdi açıkça çağırmasına izin verirken, Dispose yöntemi çözümü ile gerçekten bağlanmaz.Bu, Visual C++ içinde değiştirilmiştir.

Yeni Sözdiziminde Yıkıcılar

Yeni sözdiziminde, yıkıcı için dahili olarak Dispose yöntemine yeniden adlandırılır ve başvuru sınıfı IDispose arabirimini uygulamak için otomatik olarak genişletilir.Başka bir deyişle, Visual C++ altında bize ait sınıflar çifti aşağıdaki gibi dönüştürülür:

// internal transformation of destructor under the new syntax
__gc class A : IDisposable {
public:
   void Dispose() { 
      System::GC::SuppressFinalize(this);
      Console::WriteLine( "in ~A");
   }
};

__gc class B : public A {
public:
   void Dispose() { 
      System::GC::SuppressFinalize(this);
      Console::WriteLine( "in ~B");  
      A::Dispose(); 
   }
};

Gerek bir yok edici yeni sözdizimi altında açık bir biçimde çağırıldığında, gerekse delete öğesi bir izleme tutamacına uygulandığında arka plandaki Dispose yöntemi otomatik olarak çağırılır.Türetilmiş bir sınıfsa, temel sınıfının Dispose yönteminin bir çağrısı sentezlenmiş yöntemin kapanışına eklenir.

Ancak bu bizi tamamen belirli sonuca götürmez.Buna ulaşmak için yerel başvuru nesnelerinin ek desteği gerekir.(Bunun Yönetilen Uzantılar içinde benzer bir desteği yok, dolayısıyla bu bir çeviri sorunu değildir.)

Başvuru Nesnesi Bildirme

Visual C++ bir başvuru sınıfına ait bir nesnenin yerel yığın üzerinde veya doğrudan erişilebilir gibi bir sınıfın üyesi biçiminde bildirilmesini destekler.Dispose yöntemiyle yok edicinin ilişkilendirmesiyle birleştirildiğinde sonuç, başvuru türlerindeki sonlandırma sözdizimlerinin otomatik çağırılmasıdır.

Önce, nesne oluşturmanın bir kaynağın sınıf oluşturucusu üzerinden alınması olarak işlev görmesi için başvuru sınıfımızı tanımlarız.İkincisi, sınıf yıkıcı içinde, nesne oluşturulduğunda alınan kaynağı serbest bırakıyoruz.

public ref class R {
public:
   R() { /* acquire expensive resource */ }
   ~R() { /* release expensive resource */ }

   // … everything else …
};

Nesne, tür adı kullanılarak ancak eşlik eden şapka olmadan yerel olarak bildirilir.Bir yöntem çağırma gibi tüm nesne kullanımları, ok (->) yerine üye seçme noktası (.) aracılığıyla yapılır.Blok sonunda, Dispose'a dönüştürülmüş ilişkili bir yıkıcı, burada gösterildiği gibi otomatik olarak çağrılır:

void f() {
   R r; 
   r.methodCall();

   // r is automatically destructed here –
   // that is, r.Dispose() is invoked
}

C#'deki using deyiminde olduğu gibi, bu tüm başvuru tiplerinin CLR yığınında ayrılmaları gerektiği şeklindeki temel CLR kısıtlamasına aykırı değildir.Temel anlambilim değişmeden kalır.Kullanıcı eşit bir şekilde aşağıdakini yazabilirdi (ve bu büyük olasılıkla derleyici tarafından gerçekleştirilen iç dönüşümdür):

// equivalent implementation
// except that it should be in a try/finally clause
void f() {
   R^ r = gcnew R; 
   r->methodCall();

   delete r;
}

Yeni sözdizimi altında, yerel nesnenin ömrüne bağlı bir otomatikleştirilmiş alma/bırakma mekanizması olarak yıkıcılar kurucularla yeniden eşleşir.

Bir Açık Sonlandırıcı Bildirme

Gördüğümüz gibi, yeni sözdiziminde yıkıcı Dispose yönteminin içine sentezlenir.Buna göre, yok edici kesin bir biçimde çalıştırılmadığında, atık toplayıcı sonlandırma sırasında nesne için daha önce olduğu gibi ilişkilendirilmiş bir Finalize yöntemi bulmayacaktır.Hem yıkımı hem de sonlanmayı desteklemek için bir sonlandırıcı sağlaması için özel bir sözdizimi sunulmuştur.Örne?in:

public ref class R {
public:
   !R() { Console::WriteLine( "I am the R::finalizer()!" ); }
};

! öneki, bir sınıf yıkıcı sunan tilde (~) işaretiyle paraleldir – yani, ömür sonrası yöntemlerinin her ikisi de sınıf adına önek getiren bir belirteç içerir.Sentezlenmiş Finalize yöntemi türetilmiş bir sınıf içerisinde oluşuyorsa, temel sınıfın çağrılma Finalize yöntemi sonuna eklenir.Yıkıcı açıkça çağrılırsa sonlandırıcı bastırılır.Dönüşümü aşağıdaki gibi görünmelidir:

// internal transformation under new syntax
public ref class R {
public:
   void Finalize() {
      Console::WriteLine( "I am the R::finalizer()!" );
   }
}; 

Yönetilen C++ Uzantılarından Visual C++ 2010'a Taşıma

Altında derlenen bir C++ program için yönetilen uzantılar çalışma zamanı davranışını değiştirmiş Visual C++ zaman önemsiz olmayan yıkıcı bir başvuru sınıf içerir.Gerekli çeviri algoritması aşağıdakine benzer:

  1. Yıkıcı varsa, sınıf sonlandırıcısı olması için yeniden yazın.

  2. Dispose yöntemi varsa, sınıf yıkıcısı olması için yeniden yazın.

  3. Bir yıkıcı varsa ancak hiçbir Dispose yöntemi yoksa, ilk öğeyi gerçekleştirirken yıkıcıyı koruyun.

Kodunuzu Yönetilen Uzantılar'dan yeni söz dizimine taşırken bu geçişi gerçekleştirmeyi atlayabilirsiniz.Uygulama herhangi bir şekilde ilişkili sonlandırma yöntemlerinin yürütülmesine bağlıysa, uygulamanın davranışı hedeflediğinizden sessizce farklılık gösterecektir.

Ayrıca bkz.

Başvuru

Visual C++ üzerinde Yok Ediciler ve Sonlandırıcılar

Kavramlar

Yönetilen Türler (C++/CL)