Udostępnij za pośrednictwem


Zmiany w semantyce destruktora

Semantyka dla klasy destruktorów zmieniły się znacznie od rozszerzenia zarządzane dla języka C++ do Visual C++.

W zarządzanych rozszerzeń destruktor klasy prawo w obrębie klasy odniesienia, ale nie w klasie wartooć.Nie zmienił się to nowe elementy składni.Jednak zmieniono semantykę destruktor klasy.W tym temacie koncentruje się na przyczyny tego zmienić, a w tym artykule omówiono, jak to wpływa na tłumaczenie istniejącego kodu CLR.Jest to prawdopodobnie Najważniejsza zmiana programisty między dwiema wersjami języka.

Finalizacja deterministyczny

Zanim pamięć skojarzona z obiektem jest odzyskane przez moduł garbage collector, skojarzony Finalize , jeśli jest obecny, wywoływana jest metoda.Można traktować tej metody jako rodzaj Super-destruktora ponieważ nie jest powiązany program istnienia obiektu.Mówimy o to jako finalizacji.Harmonogram tylko kiedy lub nawet czy Finalize wywoływana jest metoda jest niezdefiniowane.Jest to, co to jest, gdy mówimy, że wyrzucania elementów bezużytecznych wykazuje finalizacji deterministyczny.

Finalizacja non deterministyczne działa dobrze z zarządzania pamięci dynamicznej.Gdy dostępna pamięć staje się rzadkie, w rzutach moduł garbage collector.W obszarze śmieci zbierane środowiska, destruktory w celu zwolnienia pamięci są niepotrzebne.Finalizacja non deterministyczne nie działa dobrze, jednak gdy obiekt utrzymuje krytycznego zasobu, takie jak połączenie z bazą danych lub blokada pewnego rodzaju.W tym przypadku firma Microsoft należy zwolnić ten zasób możliwie jak najszybciej.W świecie macierzystego, która osiąga się za pomocą pary Konstruktor/destruktora.Jak tylko kończy się okres istnienia obiektu, gdy kończy się lokalne bloku, w którym jest on zadeklarowany jako lub gdy stos unravels z powodu Wyrzucony wyjątek, destruktor wykonuje i zasób jest automatycznie zwolniony.Ta metoda działa bardzo dobrze i tęsknić jego nieobecności w obszarze rozszerzeń zarządzanych.

Z przedstawionych przez środowisko CLR jest dla klasy Dispose metody IDisposable interfejsu.Problemem jest to, że Dispose wymaga jawnego wywołania przez użytkownika.To jest podatne na błędy.Język C# zawiera skromną formą automatyzacji w formie specjalnej using instrukcji.Projekt zarządzane rozszerzenia dostarczane bez obsługi specjalnych.

Destruktory w rozszerzenia zarządzane dla języka C++

W zarządzanych rozszerzeń destruktor klasy reference jest implementowane przy użyciu następujących dwóch kroków:

  1. Nazwa destruktora dostarczone przez użytkownika zostanie zmieniona wewnętrznie do Finalize.Jeśli klasa ma klasy podstawowej (Pamiętaj, zgodnie z modelem obiektu CLR jest obsługiwana tylko pojedynczego dziedziczenia), kompilator wstrzykuje się wywołanie jego finalizatorów po wykonaniu kodu dostarczane przez użytkownika.Na przykład rozważmy następującą hierarchię proste pobranych z specyfikacją rozszerzeń zarządzanych języka:
__gc class A {
public:
   ~A() { Console::WriteLine(S"in ~A"); }
};
   
__gc class B : public A {
public:
   ~B() { Console::WriteLine(S"in ~B");  }
};

W tym przykładzie są zmieniane zarówno destruktorów Finalize.Bw Finalize ma wywołania Aw Finalize metoda dodane po wywołaniu WriteLine.Jest to, co moduł garbage collector będzie domyślnie wywoływał podczas finalizacji.Oto, co może wyglądać tej wewnętrznej transformacji:

// 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. W drugim kroku kompilator syntetyzuje wirtualny destruktor.Ten destruktora jest co naszych programów użytkownika rozszerzeń zarządzanych wywołać bezpośrednio lub za pośrednictwem aplikacji wyrażenia delete.Nigdy nie zostanie wywołana przez moduł garbage collector.

    Dwie instrukcje znajdują się w obrębie tego syntezatora destruktora.Jeden jest wywołanie GC::SuppressFinalize aby upewnić się, że nie istnieją żadne wywołania więcej Finalize.Drugi to rzeczywiste wywołanie Finalize, która reprezentuje destruktora dostarczone przez użytkownika dla tej klasy.Oto, co to może mieć następującą postać:

__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();
   }
};

Podczas gdy ta implementacja umożliwia użytkownikowi jawnie wywołać klasy Finalize metoda teraz raczej niż w danej chwili nie masz kontroli nad, to nie naprawdę blokuje przy użyciu Dispose metoda rozwiązania.To jest zmieniany w Visual C++.

Destruktory w nowe elementy składni

W nowych składni destruktor jest zmieniana wewnętrznie do Dispose metoda i klasy odwołanie jest automatycznie poszerzany, aby zaimplementować IDispose interfejsu.To znaczy, w obszarze Visual C++, nasze pary klas jest przekształcany w następujący sposób:

// 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(); 
   }
};

Gdy albo destruktora jest wywoływane jawnie w nowe elementy składni lub delete jest stosowany do uchwytu śledzenie podstawowych Dispose metoda jest wywoływana automatycznie.Jeśli jest to klasa pochodna, rozmowy z Dispose metoda klasy bazowej dodaje się pod koniec metoda syntetyzowanej.

Ale to nie uzyska NAS aż do finalizacji deterministyczny.Aby osiągnąć, który, potrzebujemy dodatkowego wsparcia obiekty lokalne odwołanie. (To nie ma analogicznych wsparcia w ramach rozszerzenia zarządzane, a więc nie jest to problem z tłumaczeniem).

DEKLARUJĄC obiektu odwołania

Visual C++popiera deklarację obiektu klasy reference na stosie lokalnych lub jako członek klasy, tak jakby był on dostępny bezpośrednio.W połączeniu ze stowarzyszenia destruktora z Dispose metoda, wynik jest automatyczne wywoływanie semantyka finalizacji na typy odwołań.

Po pierwsze definiujemy klasy Nasze odwołanie takie, że tworzenie obiektu funkcjonuje jako przejęcie zasobu za pośrednictwem jej konstruktora klasy.Po drugie w ramach destruktor klasy udostępniamy zasobów nabywane, gdy obiekt został utworzony.

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

   // … everything else …
};

Obiekt jest zadeklarowana lokalnie, przy użyciu nazwy typu, ale bez towarzyszącego kapelusz.Wszelkie zastosowania obiektu, takie jak wywoływanie metody, są wykonywane za pomocą dot wybór Państwa (.) zamiast strzałki (->).Na koniec bloku skojarzone destruktor, przekształcony w Dispose, jest wywoływana automatycznie, jak pokazano poniżej:

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

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

Podobnie jak w przypadku using instrukcja w języku C#, to nie mnóstwem podstawowe ograniczenie CLR wszystkie typy odwołań muszą być przydzielone na stercie CLR.Semantyka podstawowych, pozostają niezmienione.Użytkownik może równoważnie zapisane następujące (i jest to prawdopodobnie wewnętrznej transformacji, wykonywane przez kompilator):

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

   delete r;
}

W efekcie pod nową składnię, destruktory są ponownie w połączeniu z konstruktorów jako automatyczne nabycia/release mechanizm związany okres istnienia obiektu lokalnego.

DEKLARUJĄC jawne Finalize

W nowych składni, jak widzieliśmy, destruktor jest otrzymywanych w wyniku syntezy do Dispose metody.Oznacza to, że gdy destruktor nie zostanie wywołany jawnie, moduł garbage collector podczas finalizacji, nie, jak wcześniej znajdzie skojarzony Finalize metodę obiektu.Aby obsługiwać zarówno zniszczenia i finalizacja, wprowadziliśmy specjalnej składni za dostarczanie finalizatorów.Na przykład:

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

! Prefiks jest analogiczne do tyldy (~), w której wprowadzono destruktor klasy – oznacza to, że obie metody POST-okres istnienia mają token tworzenie prefiksu nazwy klasy.Jeśli syntetyzowanej Finalize metoda występuje w klasie pochodnej, wywołanie klasy bazowej Finalize metoda dodaje się na końcu.Jeśli zostanie on zostanie wywołany jawnie, finalizatorów jest pomijane.Oto, co transformacja może mieć następującą postać:

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

Przenoszenie z rozszerzenia zarządzane dla języka C++ do programu Visual C++ 2010

Zachowanie zarządzane rozszerzenia program w języku C++ runtime zostanie zmieniona, gdy jest skompilowany w obszarze Visual C++ gdy klasa odwołanie zawiera-trivial destruktora.Algorytm tłumaczenia wymagana jest podobny do następującego:

  1. Jeśli występuje destruktora edycji to za finalizatorów klasy.

  2. Jeśli Dispose metoda jest obecny, który przerobienia na destruktor klasy.

  3. Jeśli destruktora jest obecny, ale ma nie Dispose metoda, zachowują destruktor podczas wykonywania pierwszego elementu.

Podczas przenoszenia kodu aplikacji z rozszerzeń zarządzanych do nowej składni, mogą pominąć wykonywanie tej transformacji.Jeśli aplikacja zależało w jakiś sposób wykonania metody finalizacji skojarzone, zachowanie aplikacji po cichu będą różne niż ten, który powinien.

Zobacz też

Informacje

Destruktory i finalizatory w języku Visual C++

Koncepcje

Typy zarządzane (C++/CL)