Udostępnij za pośrednictwem


Semantyka typów wartości

Typ semantyka zostały zmienione z rozszerzenia zarządzane dla języka C++ do wartości Visual C++.

W tym miejscu jest typem kanonicznym prostą wartością używany w rozszerzenia zarządzane dla języka C++ spec:

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

W zarządzanych rozszerzeń możemy mieć cztery warianty składni typu wartości (gdzie formularze 2 i 3 są takie same semantycznie):

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 

Wywoływanie metod wirtualnych dziedziczone

Form (1)jest obiektem kanonicznej wartość i dostatecznie dobrze rozumie się, z wyjątkiem przypadków, kiedy ktoś próbuje wywołać metody odziedziczonej wirtualnego takich jak ToString().Na przykład:

v.ToString(); // error!

W celu wywołania tej metody, ponieważ nie jest ona zastępowana w V, kompilator musi mieć dostęp do wirtualnej tabeli skojarzonej klasy podstawowej.Ponieważ typów wartości są w stanie pamięci masowej bez skojarzonego wskaźnik do swojej tabeli wirtualnych (vptr), wymagane jest, aby v zapakowane.W projekcie rozszerzeń zarządzanych języka niejawna bokserskie nie jest obsługiwane, ale muszą być jawnie określone przez programistę, podobnie jak w przypadku

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

Podstawowym motywem ten projekt jest pedagogicznych: leżące u podstaw mechanizmu musi być widoczna dla programisty, tak, że będzie ona zrozumieć "koszt" nie dostarcza wystąpienie w ramach jej typ wartości.Były V zawiera wystąpienie ToString, bokserskie nie powinno być konieczne.

Leksykalne złożoność boksie jawnie obiektu, ale nie podstawowej kosztu bokserskie, sama jest usuwany w nowe elementy składni:

v.ToString(); // new syntax

ale jest związany z ewentualnie wprowadzającej w błąd projektanta klasy co do kosztów o nieprzewidzianych jawnego wystąpienia ToString metoda w V.Przyczyna preferowanie niejawna bokserskie zakłada, że podczas gdy jest zazwyczaj tylko jeden Projektant klasy, nieograniczoną liczbę użytkowników, z których żadna nie miałby prawo do modyfikowania V do wyeliminowania ewentualnie uciążliwe pole jawne.

Kryteria określania, czy należy podać wystąpienie nadrzędne ToString wewnątrz wartości klasa powinna być częstotliwości oraz miejsca jego zastosowań.Jeśli jest to bardzo rzadko, jest oczywiście niewielkie korzyści w jego definicji.Podobnie jeśli zostanie wywołana w obszarach innych niż wydajnych aplikacji, dodanie go także nie wymierne doda do ogólnej wydajności aplikacji.Alternatywnie uchwyt śledzenia można przechowywać w ramce wartości i połączeń za pośrednictwem dojścia nie wymaga boksie.

Nie ma już konstruktora domyślna wartość klasy

Inna różnica z typem wartości między rozszerzeń zarządzanych i nowe elementy składni jest usunięcie obsługę domyślnego konstruktora.Dlatego istnieją sytuacje podczas wykonywania, w którym CLR można utworzyć wystąpienie typu wartości bez wywoływania konstruktora domyślnego skojarzone.Oznacza to próba obsługuje domyślnego konstruktora w ramach typu wartości w obszarze zarządzane rozszerzenia może w praktyce nie można zagwarantować.Biorąc pod uwagę że braku gwarancji, uważano lepiej byłoby całkowicie usunąć wsparcie, a nie być deterministyczny podczas jej stosowania.

To nie jest tak źle, jak początkowo mogłoby się wydawać.To dlatego każdy obiekt typu wartości jest automatycznie wyzerowany (każdego typu jest inicjowana na wartość domyślną).W rezultacie członkowie lokalnej instancji nigdy nie są niezdefiniowane.W tym sensie utraty możliwości definiowania trivial domyślnego konstruktora nie jest tak naprawdę straty w ogóle- i w rzeczywistości jest bardziej efektywne, kiedy wykonywane przez środowisko CLR.

Problem jest, gdy użytkownik zarządzanych rozszerzeń definiuje-trivial domyślnego konstruktora.To nie ma mapowania na nowe elementy składni.Kod w Konstruktorze będą musiały zostać poddane migracji do metody o nazwie inicjowania, która następnie musi być jawnie wywołana przez użytkownika.

W przeciwnym razie niezmieniona deklarację obiektu typu wartości w nowe elementy składni.Wadą tego jest to, czy typów wartości nie są wystarczające do opakowywania macierzyste typy z następujących powodów:

  • Nie ma destruktora w ramach typu wartości nie są obsługiwane.Oznacza to, że jest sposobem zautomatyzowania zestaw akcje wywoływane przez koniec okresu istnienia obiektu.

  • Macierzysta klasa mogą być zawarte tylko w ramach typu zarządzanego jako wskaźnik, który jest następnie przydzielana na sterty macierzystej.

Chcielibyśmy Zawijaj małych macierzysta klasa w typu wartości, a nie typu odwołania do uniknięcia alokację sterty podwójne: sterty macierzystej do przechowywania typu macierzystego i sterty CLR do przechowywania otoki zarządzanej.Zawijanie macierzysta klasa w ramach typu wartości umożliwia uniknięcie zarządzanego stosu, ale zapewnia sposobem zautomatyzowania regeneracji pamięci sterty macierzystej.Typy odwołań są tylko jest to możliwe typu zarządzanego, w ramach którego oblewania-trivial macierzystych klas.

Wskaźniki wewnętrzne

Form (2)i Form (3) powyżej mogą adresować niemal wszystko w tym świecie lub następnego (czyli wszystko zarządzanym lub macierzystym).Tak na przykład, następujące są dozwolone w zarządzanych rozszerzeń:

__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

Tak V* mogą adresować lokalizacji lokalnego bloku (i mogą być nieaktualne), w zakresie globalnym, w macierzystych stosu (na przykład, jeśli obiekt jest on skierowany został już usunięty), w ramach sterty CLR (i dlatego będą śledzone Jeśli powinny być przenoszone podczas wyrzucania elementów bezużytecznych) oraz w ramach wnętrza obiektu odwołania na stercie CLR (wnętrza wskaźnikajak jest to, również w przezroczysty sposób śledzenia).

W zarządzanych rozszerzeń nie istnieje sposób wydzielić macierzystego aspektów V*; oznacza to jest ona traktowana w jego włącznie, która zajmuje się na prawo od niego adresowania obiektu lub podobiektów na stercie zarządzanych.

W nowych składni wskaźnika typu wartość jest brana pod uwagę na dwa typy: V*, który jest ograniczony do lokalizacji sterty-CLR i wskaźnik wnętrza interior_ptr<V>, która pozwala na, ale nie jest wymagane adres w zarządzanych sterty.

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

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

Form (2)i Form (3) zarządzanych rozszerzeń mapować do interior_ptr<V>.Form (4)jest to dojście śledzenia.Dotyczy on cały obiekt, który zostały zapakowane w zarządzanych sterty.Jest tłumaczony w nowe elementy składni do V^,

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

Następujące deklaracje rozszerzenia zarządzane wszystkie mapowane do wnętrza wskaźniki w nowe elementy składni. (Są one typy wartości w ramach System nazw.)

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

Wbudowane typy nie są uważane za typy zarządzane, mimo że służą one jako aliasy do typów w System obszaru nazw.Tak więc następujące mapowania uznać za prawdziwe między rozszerzeń zarządzanych i nowe elementy składni:

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

Przy tłumaczeniu V* w programie istniejących strategii najbardziej umiarkowaną jest zawsze zwrócić do interior_ptr<V>.Jest to, jak była traktowana w obszarze rozszerzeń zarządzanych.W nowych składni programista ma możliwość ograniczenia typu wartości na adresy niezarządzanych sterty przez określenie V* a nie wewnętrzne wskaźnika.Jeśli na tłumaczenie programu, możesz zrobić przechodnie zamknięcia jego zastosowań i upewnij się, że nie przypisany adres jest w ramach zarządzanego stosu, a następnie pozostawić je jako V* jest w porządku.

Przypinanie wskaźniki

Moduł garbage collector opcjonalnie może przenieść obiektów znajdujących się w sterty CLR do różnych lokalizacji w obrębie sterty, zwykle podczas fazy kompaktowania.Ten ruch nie stanowi to problemu do śledzenia dojść, śledzenie odwołań i wewnętrzne wskaźniki, które przezroczysty aktualizacji tych podmiotów.Ten ruch jest jednak problem, jeśli użytkownik przejdzie adresu obiektu na stercie CLR spoza środowiska wykonawczego.W tym przypadku lotnych ruch obiektu jest może spowodować błąd czasu wykonywania.Do zwolnienia obiektów takich jak te przed przenoszeniem możemy musi lokalnie przypiąć je do ich lokalizacji dla zakresu ich stosowania poza.

W zarządzanych rozszerzeń Przypinanie wskaźnik jest zadeklarowany przez kwalifikujących się deklaracja wskaźnik z __pin słowa kluczowego.Oto przykład nieco zmodyfikowany ze specyfikacji zarządzane rozszerzenia:

__gc struct H { int j; };

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

W nowym projekcie języka przypinania wskaźnik jest zadeklarowany ze składni analogiczna do wnętrza wskaźnika.

ref struct H
{
public:
   int j;
};

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

   // …
}

Wskaźnik przypinania pod nowe elementy składni jest szczególnym przypadkiem wewnętrznego wskaźnika.Ograniczenia oryginalne na wskaźnik przypinania pozostają.Na przykład nie można użyć jako parametru lub zwracany typ metody; może być deklarowany tylko na obiekt lokalny.Liczba dodatkowych ograniczeń, jednak zostały dodane w nowe elementy składni.

Wartością domyślną przypinania wskaźnika jest nullptr, nie 0.A pin_ptr<> nie może zostać zainicjowany lub przydzielony 0.Wszystkie przydziały 0 w istniejący kod będzie musiał zostać zmieniony na nullptr.

Przypinania wskaźnik mocy rozszerzeń zarządzanych był dopuszczony do całego obiektu, jak w poniższym przykładzie podjęta ze specyfikacji zarządzane rozszerzenia adresu:

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

W nowych składni przypinanie cały obiekt zwrócony przez new wyrażenie nie jest obsługiwane.Raczej adres członka wnętrza musi zostać unieruchomiony.Na przykład:

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

Zobacz też

Informacje

Klasy i strukturach (zarządzanych)

interior_ptr

pin_ptr

Koncepcje

Typy wartości i ich zachowania (C++/CLI)