Vorgehensweise: Definieren und Verarbeiten von Klassen und Strukturen (C++ / CLI)How to: Define and Consume Classes and Structs (C++/CLI)

In diesem Artikel zeigt, wie definiert und verwendet eine benutzerdefinierte Verweistypen und Werttypen in C++ / CLI.This article shows how to define and consume user-defined reference types and value types in C++/CLI.

InhaltContents

ObjektinstanziierungObject instantiation

Implizit abstrakte KlassenImplicitly abstract classes

TypsichtbarkeitType visibility

MembersichtbarkeitMember visibility

Öffentliche und private systemeigene KlassenPublic and private native classes

Statische KonstruktorenStatic constructors

Semantik dieses ZeigersSemantics of the this pointer

Ausblenden nach Signatur verdeckte FunktionenHide-by-signature functions

KopierkonstruktorenCopy constructors

Destruktoren und FinalizerDestructors and finalizers

ObjektinstanziierungObject instantiation

Referenztypen (Ref) können nur auf dem verwalteten Heap, nicht auf dem Stapel oder dem systemeigenen Heap instanziiert werden.Reference (ref) types can only be instantiated on the managed heap, not on the stack or on the native heap. Werttypen können auf dem Stapel oder im verwalteten Heap instanziiert werden.Value types can be instantiated on the stack or the managed heap.

// mcppv2_ref_class2.cpp
// compile with: /clr
ref class MyClass {
public:
   int i;

   // nested class
   ref class MyClass2 {
   public:
      int i;
   };

   // nested interface
   interface struct MyInterface {
      void f();
   };
};

ref class MyClass2 : public MyClass::MyInterface {
public:
   virtual void f() {
      System::Console::WriteLine("test");
   }
};

public value struct MyStruct {
   void f() {
      System::Console::WriteLine("test");
   }
};

int main() {
   // instantiate ref type on garbage-collected heap
   MyClass ^ p_MyClass = gcnew MyClass;
   p_MyClass -> i = 4;

   // instantiate value type on garbage-collected heap
   MyStruct ^ p_MyStruct = gcnew MyStruct;
   p_MyStruct -> f();

   // instantiate value type on the stack
   MyStruct p_MyStruct2;
   p_MyStruct2.f();

   // instantiate nested ref type on garbage-collected heap
   MyClass::MyClass2 ^ p_MyClass2 = gcnew MyClass::MyClass2;
   p_MyClass2 -> i = 5;
}

Implizit abstrakte KlassenImplicitly abstract classes

Ein implizit abstrakte Klasse kann nicht instanziiert werden.An implicitly abstract class can't be instantiated. Eine Klasse ist implizit abstrakt, wenn der Basistyp der Klasse eine Schnittstelle ist und die Klasse nicht alle Memberfunktionen der Schnittstelle implementiert.A class is implicitly abstract if the base type of the class is an interface and the class does not implement all of the interface's member functions.

Wenn Objekte nicht aus einer Klasse erstellt werden können, die von einer Schnittstelle abgeleitet sind, könnte dies daran liegen, dass die Klasse implizit abstrakt ist.If you are unable to construct objects from a class that's derived from an interface, the reason might be that the class is implicitly abstract. Weitere Informationen zu abstrakten Klassen finden Sie unter abstrakte.For more information about abstract classes, see abstract.

Das folgende Codebeispiel zeigt, dass die MyClass-Klasse nicht instanziiert werden kann, da die MyClass::func2-Funktion nicht implementiert ist.The following code example demonstrates that the MyClass class cannot be instantiated because function MyClass::func2 is not implemented. Damit das Beispiel kompiliert werden kann, heben Sie die Auskommentierung der MyClass::func2-Funktion auf.To enable the example to compile, uncomment MyClass::func2.

// mcppv2_ref_class5.cpp
// compile with: /clr
interface struct MyInterface {
   void func1();
   void func2();
};

ref class MyClass : public MyInterface {
public:
   void func1(){}
   // void func2(){}
};

int main() {
   MyClass ^ h_MyClass = gcnew MyClass;   // C2259
                                          // To resolve, uncomment MyClass::func2.
}

TypsichtbarkeitType visibility

Sie können die Sichtbarkeit von CLR-Typen (Common Language Runtime) so steuern, dass bei Verweis auf eine Assembly die Typen in der Assembly sichtbar oder außerhalb der Assembly nicht sichtbar sein können.You can control the visibility of common language runtime (CLR) types so that, if an assembly is referenced, types in the assembly can be visible or not visible outside the assembly.

public Gibt an, dass ein Typ für jede Quelldatei sichtbar ist, enthält eine #using Direktive für die Assembly, die den Typ enthält.public indicates that a type is visible to any source file that contains a #using directive for the assembly that contains the type. private Gibt an, dass ein Typ nicht für Quelldateien sichtbar ist, die enthalten eine #using Direktive für die Assembly, die den Typ enthält.private indicates that a type is not visible to source files that contain a #using directive for the assembly that contains the type. Allerdings sind private Typen in der gleichen Assembly sichtbar.However, private types are visible within the same assembly. Standardmäßig ist die Sichtbarkeit für eine Klasse auf private eingestellt.By default, the visibility for a class is private.

Standardmäßig vor Visual Studio 2005 unterstützten systemeigene Typen für öffentlichen Zugriff außerhalb der Assembly.By default prior to Visual Studio 2005, native types had public accessibility outside the assembly. Aktivieren Sie Compilerwarnung (Stufe 1) C4692 , damit Sie erkennen, wo private systemeigene Typen falsch verwendet werden.Enable Compiler Warning (level 1) C4692 to help you see where private native types are used incorrectly. Verwenden der Make_public Pragma, um den öffentlichen Zugriff auf einen systemeigenen Typ in einer Quellcodedatei zu ermöglichen, die Sie nicht ändern können.Use the make_public pragma to give public accessibility to a native type in a source code file that you can't modify.

Weitere Information finden Sie unter #using Directive (#using-Direktive).For more information, see #using Directive.

Das folgende Beispiel zeigt, wie Typen deklariert und ihr Zugriff angegeben werden können und wie dann in der Assembly auf diese Typen zugegriffen werden kann.The following sample shows how to declare types and specify their accessibility, and then access those types inside the assembly. Wenn auf eine Assembly, die private Typen enthält, mithilfe von #using verwiesen wird, sind nur öffentliche Typen in der Assembly sichtbar.Of course, if an assembly that has private types is referenced by using #using, only public types in the assembly are visible.

// type_visibility.cpp
// compile with: /clr
using namespace System;
// public type, visible inside and outside assembly
public ref struct Public_Class {
   void Test(){Console::WriteLine("in Public_Class");}
};

// private type, visible inside but not outside assembly
private ref struct Private_Class {
   void Test(){Console::WriteLine("in Private_Class");}
};

// default accessibility is private
ref class Private_Class_2 {
public:
   void Test(){Console::WriteLine("in Private_Class_2");}
};

int main() {
   Public_Class ^ a = gcnew Public_Class;
   a->Test();

   Private_Class ^ b = gcnew Private_Class;
   b->Test();

   Private_Class_2 ^ c = gcnew Private_Class_2;
   c->Test();
}

AusgabeOutput

in Public_Class
in Private_Class
in Private_Class_2

Nun kann das vorherige Beispiel umgeschrieben werden, damit es als DLL erstellt wird.Now, let's rewrite the previous sample so that it is built as a DLL.

// type_visibility_2.cpp
// compile with: /clr /LD
using namespace System;
// public type, visible inside and outside the assembly
public ref struct Public_Class {
   void Test(){Console::WriteLine("in Public_Class");}
};

// private type, visible inside but not outside the assembly
private ref struct Private_Class {
   void Test(){Console::WriteLine("in Private_Class");}
};

// by default, accessibility is private
ref class Private_Class_2 {
public:
   void Test(){Console::WriteLine("in Private_Class_2");}
};

Im folgenden Beispiel wird der Zugriff auf Typen außerhalb der Assembly veranschaulicht.The next sample shows how to access types outside the assembly. In diesem Beispiel verwendet der Client die Komponente, die im vorherigen Beispiel erstellt wurde.In this sample, the client consumes the component that's built in the previous sample.

// type_visibility_3.cpp
// compile with: /clr
#using "type_visibility_2.dll"
int main() {
   Public_Class ^ a = gcnew Public_Class;
   a->Test();

   // private types not accessible outside the assembly
   // Private_Class ^ b = gcnew Private_Class;
   // Private_Class_2 ^ c = gcnew Private_Class_2;
}

AusgabeOutput

in Public_Class

MembersichtbarkeitMember visibility

Sie können den Zugriff auf einen Member einer öffentlichen Klasse aus derselben Assembly unterschiedlich gestalten als den Zugriff darauf von außerhalb der Assembly. Verwenden Sie hierzu Paare der public-, protected- und private-Zugriffsspezifizierer.You can make access to a member of a public class from within the same assembly different than access to it from outside the assembly by using pairs of the access specifiers public, protected, and private

In dieser Tabelle werden die Auswirkungen der unterschiedlichen Zugriffsspezifizierer zusammengefasst:This table summarizes the effect of the various access specifiers:

BezeichnerSpecifier EffektEffect
publicpublic Auf den Member kann innerhalb und außerhalb der Assembly leicht zugegriffen werden.Member is accessible inside and outside the assembly. Finden Sie unter öffentliche für Weitere Informationen.See public for more information.
privateprivate Auf den Member kann weder innerhalb noch außerhalb der Assembly zugegriffen werden.Member is not accessible, neither inside nor outside the assembly. Finden Sie unter private für Weitere Informationen.See private for more information.
protectedprotected Auf den Member kann innerhalb und außerhalb der Assembly zugegriffen werden. Dies gilt jedoch nur für abgeleitete Typen.Member is accessible inside and outside the assembly, but only to derived types. Finden Sie unter geschützt für Weitere Informationen.See protected for more information.
internalinternal Der Member ist öffentlich innerhalb der Assembly und privat außerhalb der Assembly.Member is public inside the assembly but private outside the assembly. internal ist ein kontextbezogenes Schlüsselwort.internal is a context-sensitive keyword. Weitere Informationen finden Sie unter Kontextbezogene Schlüsselwörter.For more information, see Context-Sensitive Keywords.
Öffentliche geschützten - oder - Zeichen geschützte öffentlichepublic protected -or- protected public Der Member ist öffentlich innerhalb der Assembly und geschützt außerhalb der Assembly.Member is public inside the assembly but protected outside the assembly.
Private geschützte - oder - Zeichen geschützten privatenprivate protected -or- protected private Der Member ist geschützt innerhalb der Assembly und privat außerhalb der Assembly.Member is protected inside the assembly but private outside the assembly.

Das folgende Beispiel zeigt einen öffentlichen Typ, der Member enthält, die mit verschiedenen Barrierefreiheiten deklariert wurden, und veranschaulicht dann den Zugriff auf diese Member aus der Assembly heraus.The following sample shows a public type that has members that are declared with the different accessibilities, and then shows the accessing of those members from inside the assembly.

// compile with: /clr
using namespace System;
// public type, visible inside and outside the assembly
public ref class Public_Class {
public:
   void Public_Function(){System::Console::WriteLine("in Public_Function");}

private:
   void Private_Function(){System::Console::WriteLine("in Private_Function");}

protected:
   void Protected_Function(){System::Console::WriteLine("in Protected_Function");}

internal:
   void Internal_Function(){System::Console::WriteLine("in Internal_Function");}

protected public:
   void Protected_Public_Function(){System::Console::WriteLine("in Protected_Public_Function");}

public protected:
   void Public_Protected_Function(){System::Console::WriteLine("in Public_Protected_Function");}

private protected:
   void Private_Protected_Function(){System::Console::WriteLine("in Private_Protected_Function");}

protected private:
   void Protected_Private_Function(){System::Console::WriteLine("in Protected_Private_Function");}
};

// a derived type, calls protected functions
ref struct MyClass : public Public_Class {
   void Test() {
      Console::WriteLine("=======================");
      Console::WriteLine("in function of derived class");
      Protected_Function();
      Protected_Private_Function();
      Private_Protected_Function();
      Console::WriteLine("exiting function of derived class");
      Console::WriteLine("=======================");
   }
};

int main() {
   Public_Class ^ a = gcnew Public_Class;
   MyClass ^ b = gcnew MyClass;
   a->Public_Function();
   a->Protected_Public_Function();
   a->Public_Protected_Function();

   // accessible inside but not outside the assembly
   a->Internal_Function();

   // call protected functions
   b->Test();

   // not accessible inside or outside the assembly
   // a->Private_Function();
}

AusgabeOutput

in Public_Function
in Protected_Public_Function
in Public_Protected_Function
in Internal_Function
=======================
in function of derived class
in Protected_Function
in Protected_Private_Function
in Private_Protected_Function
exiting function of derived class
=======================

Nun kann das vorherige Beispiel als DLL erstellt werden.Now let's build the previous sample as a DLL.

// compile with: /clr /LD
using namespace System;
// public type, visible inside and outside the assembly
public ref class Public_Class {
public:
   void Public_Function(){System::Console::WriteLine("in Public_Function");}

private:
   void Private_Function(){System::Console::WriteLine("in Private_Function");}

protected:
   void Protected_Function(){System::Console::WriteLine("in Protected_Function");}

internal:
   void Internal_Function(){System::Console::WriteLine("in Internal_Function");}

protected public:
   void Protected_Public_Function(){System::Console::WriteLine("in Protected_Public_Function");}

public protected:
   void Public_Protected_Function(){System::Console::WriteLine("in Public_Protected_Function");}

private protected:
   void Private_Protected_Function(){System::Console::WriteLine("in Private_Protected_Function");}

protected private:
   void Protected_Private_Function(){System::Console::WriteLine("in Protected_Private_Function");}
};

// a derived type, calls protected functions
ref struct MyClass : public Public_Class {
   void Test() {
      Console::WriteLine("=======================");
      Console::WriteLine("in function of derived class");
      Protected_Function();
      Protected_Private_Function();
      Private_Protected_Function();
      Console::WriteLine("exiting function of derived class");
      Console::WriteLine("=======================");
   }
};

Im folgenden Beispiel wird die im vorherigen Beispiel erstellte Komponente verwendet und dadurch der Zugriff auf Member von außerhalb der Assembly veranschaulicht.The following sample consumes the component that's created in the previous sample, and thereby shows how to access the members from outside the assembly.

// compile with: /clr
#using "type_member_visibility_2.dll"
using namespace System;
// a derived type, calls protected functions
ref struct MyClass : public Public_Class {
   void Test() {
      Console::WriteLine("=======================");
      Console::WriteLine("in function of derived class");
      Protected_Function();
      Protected_Public_Function();
      Public_Protected_Function();
      Console::WriteLine("exiting function of derived class");
      Console::WriteLine("=======================");
   }
};

int main() {
   Public_Class ^ a = gcnew Public_Class;
   MyClass ^ b = gcnew MyClass;
   a->Public_Function();

   // call protected functions
   b->Test();

   // can't be called outside the assembly
   // a->Private_Function();
   // a->Internal_Function();
   // a->Protected_Private_Function();
   // a->Private_Protected_Function();
}

AusgabeOutput

in Public_Function
=======================
in function of derived class
in Protected_Function
in Protected_Public_Function
in Public_Protected_Function
exiting function of derived class
=======================

Öffentliche und private systemeigene KlassenPublic and private native classes

Ein systemeigener Typ kann von einem verwalteten Typ referenziert werden.A native type can be referenced from a managed type. Beispielsweise kann eine Funktion in einem verwalteten Typ einen Parameter aufnehmen, dessen Typ eine systemeigene Struktur ist.For example, a function in a managed type can take a parameter whose type is a native struct. Wenn der verwaltete Typ und die verwaltete Funktion in einer Assembly öffentlich sind, muss der systemeigene Typ ebenfalls öffentlich sein.If the managed type and function are public in an assembly, then the native type must also be public.

// native type
public struct N {
   N(){}
   int i;
};

Anschließend erstellen Sie die Quellcodedatei, die den systemeigenen Typ verwendet:Next, create the source code file that consumes the native type:

// compile with: /clr /LD
#include "mcppv2_ref_class3.h"
// public managed type
public ref struct R {
   // public function that takes a native type
   void f(N nn) {}
};

Kompilieren Sie nun einen Client:Now, compile a client:

// compile with: /clr
#using "mcppv2_ref_class3.dll"

#include "mcppv2_ref_class3.h"

int main() {
   R ^r = gcnew R;
   N n;
   r->f(n);
}

Statische KonstruktorenStatic constructors

Ein CLR-Typ, z. B. eine Klasse oder Struktur, kann einen statischen Konstruktor enthalten, der zum Initialisieren von statischen Datenmembern verwendet werden kann.A CLR type—for example, a class or struct—can have a static constructor that can be used to initialize static data members. Ein statischer Konstruktor wird höchstens einmal aufgerufen. Der Aufruf erfolgt, bevor auf einen statischen Member des Typs zum ersten Mal zugegriffen wird.A static constructor is called at most once, and is called before any static member of the type is accessed the first time.

Ein Instanzkonstruktor wird immer nach einen statischen Konstruktor ausgeführt.An instance constructor always runs after a static constructor.

Der Compiler kann einen Aufruf nicht an einen Konstruktor leiten, wenn die Klasse über einen statischen Konstruktor verfügt.The compiler cannot inline a call to a constructor if the class has a static constructor. Der Compiler kann einen Aufruf nicht an eine Memberfunktion leiten, wenn die Klasse ein Werttyp ist, über einen statischen Konstruktor verfügt und keine Instanzkonstruktor hat.The compiler cannot inline a call to any member function if the class is a value type, has a static constructor, and does not have an instance constructor. Die CLR-Funktion kann den Aufruf leiten, der Compiler jedoch nicht.The CLR may inline the call, but the compiler cannot.

Definieren Sie einen statischen Konstruktor als private Memberfunktion, da er nur von der CLR-Funktion aufgerufen werden soll.Define a static constructor as a private member function, because it is meant to be called only by the CLR.

Weitere Informationen zu statischen Konstruktoren finden Sie unter Vorgehensweise: Definieren ein statischen Schnittstellenkonstruktors (C++ / CLI) .For more information about static constructors, see How to: Define an Interface Static Constructor (C++/CLI) .

// compile with: /clr
using namespace System;

ref class MyClass {
private:
   static int i = 0;

   static MyClass() {
      Console::WriteLine("in static constructor");
      i = 9;
   }

public:
   static void Test() {
      i++;
      Console::WriteLine(i);
   }
};

int main() {
   MyClass::Test();
   MyClass::Test();
}

AusgabeOutput

in static constructor
10
11

Semantik dieses ZeigersSemantics of the this pointer

Wenn Sie Visual C++ zum Definieren von Typen verwenden, ist der this-Zeiger in einem Referenztyp vom Typ "Handle".When you are using Visual C++ to define types, the this pointer in a reference type is of type "handle". Der this-Zeiger in einen Werttyp ist vom Typ "innerer Zeiger".The this pointer in a value type is of type "interior pointer".

Diese unterschiedliche Semantik des this-Zeigers kann beim Aufrufen eines Standardindexers ein unerwartetes Verhalten verursachen.These different semantics of the this pointer can cause unexpected behavior when a default indexer is called. Im folgenden Beispiel wird die korrekte Methode zum Zugriff auf einen Standardindexer in einem Referenz- und in einem Werttyp veranschaulicht.The next example shows the correct way to access a default indexer in both a ref type and a value type.

Weitere Informationen finden Sie unterFor more information, see

// compile with: /clr
using namespace System;

ref struct A {
   property Double default[Double] {
      Double get(Double data) {
         return data*data;
      }
   }

   A() {
      // accessing default indexer
      Console::WriteLine("{0}", this[3.3]);
   }
};

value struct B {
   property Double default[Double] {
      Double get(Double data) {
         return data*data;
      }
   }
   void Test() {
      // accessing default indexer
      Console::WriteLine("{0}", this->default[3.3]);
   }
};

int main() {
   A ^ mya = gcnew A();
   B ^ myb = gcnew B();
   myb->Test();
}

AusgabeOutput

10.89
10.89

Ausblenden nach Signatur verdeckte FunktionenHide-by-signature functions

In Standard-C++ wird eine Funktion in einer Basisklasse durch eine Funktion ausgeblendet, die den gleichen Namen in einer abgeleiteten Klasse hat, auch wenn die abgeleitete Klassenfunktion nicht dieselbe Anzahl bzw. Art von Parametern aufweist.In standard C++, a function in a base class is hidden by a function that has the same name in a derived class, even if the derived-class function does not have the same number or kind of parameters. Dies wird als bezeichnet nach Namen verdeckte Semantik.This is referred to as hide-by-name semantics. In einem Referenztyp kann eine Funktion in einer Basisklasse nur durch eine Funktion in einer abgeleiteten Klasse ausgeblendet werden, wenn sowohl der Name als auch die Parameterliste identisch sind.In a reference type, a function in a base class can only be hidden by a function in a derived class if both the name and the parameter list are the same. Dies bezeichnet man als Ausblenden nach Signatur verdeckte Semantik.This is known as hide-by-signature semantics.

Eine Klasse gilt als nach Signatur verdeckte Klasse, wenn alle zugehörigen Funktionen in den Metadaten als hidebysig gekennzeichnet sind.A class is considered a hide-by-signature class when all of its functions are marked in the metadata as hidebysig. Standardmäßig werden alle Klassen, die unter erstellt werden "/ CLR" haben hidebysig Funktionen.By default, all classes that are created under /clr have hidebysig functions. Wenn eine Klasse über hidebysig-Funktionen verfügt, blendet der Compiler keine Funktionen in den direkten Basisklassen nach Namen aus. Wenn der Compiler jedoch eine nach Namen verdeckte Klasse in einer Vererbungskette erkennt, wird dieses nach Namen verdeckte Verhalten fortgesetzt.When a class has hidebysig functions, the compiler doesn't hide functions by name in any direct base classes, but if the compiler encounters a hide-by-name class in an inheritance chain, it continues that hide-by-name behavior.

Wenn eine Funktion unter der nach Signatur verdeckten Semantik für ein Objekt aufgerufen wird, identifiziert der Compiler die am meisten abgeleitete Klasse, die eine Funktion enthält, die den Funktionsaufruf erfüllen kann.Under hide-by-signature semantics, when a function is called on an object, the compiler identifies the most derived class that contains a function that could satisfy the function call. Wenn es nur eine Funktion in der Klasse gibt, die den Aufruf erfüllen kann, ruft der Compiler diese Funktion auf.If there is only one function in the class that could satisfy the call, the compiler calls that function. Wenn es mehr als eine Funktion in der Klasse gibt, die den Aufruf erfüllen kann, verwendet der Compiler die Überladungsauflösungsregeln zum Ermitteln, welche Funktion aufgerufen werden soll.If there is more than one function in the class that could satisfy the call, the compiler uses overload resolution rules to determine which function to call. Weitere Informationen zu den Überladungsregeln finden Sie unter Funktionsüberladung.For more information about overload rules, see Function Overloading.

Für einen angegebenen Funktionsaufruf kann eine Funktion in einer Basisklasse eine Signatur haben, mit der sie etwas besser übereinstimmt als eine Funktion in einer abgeleiteten Klasse.For a given function call, a function in a base class might have a signature that makes it a slightly better match than a function in a derived class. Wenn die Funktion jedoch explizit für ein Objekt der abgeleiteten Klasse aufgerufen wurde, wird die Funktion in der abgeleiteten Klasse aufgerufen.However, if the function was explicitly called on an object of the derived class, the function in the derived class is called.

Da der Rückgabewert nicht als Teil einer Signatur der Funktion gilt, wird eine Basisklassenfunktion ausgeblendet, wenn sie denselben Namen hat und über die dieselbe Anzahl und Art von Argumenten wie eine abgeleitete Klassenfunktion verfügt, auch wenn sie sich vom Typ des Rückgabewerts unterscheidet.Because the return value is not considered part of a function's signature, a base-class function is hidden if it has the same name and takes the same number and kind of arguments as a derived-class function, even if it differs in the type of the return value.

Das folgende Beispiel zeigt, dass eine Funktion in einer Basisklasse nicht von einer Funktion in einer abgeleiteten Klasse ausgeblendet wird.The following sample shows that a function in a base class is not hidden by a function in a derived class.

// compile with: /clr
using namespace System;
ref struct Base {
   void Test() {
      Console::WriteLine("Base::Test");
   }
};

ref struct Derived : public Base {
   void Test(int i) {
      Console::WriteLine("Derived::Test");
   }
};

int main() {
   Derived ^ t = gcnew Derived;
   // Test() in the base class will not be hidden
   t->Test();
}

AusgabeOutput

Base::Test

Das nächste Beispiel zeigt, dass Microsoft C++ Compiler Ruft eine Funktion in der am stärksten abgeleitete Klasse, auch wenn eine Konvertierung erforderlich ist, eine oder mehrere Parameter entsprechend – und nicht in einer Basisklasse, die eine bessere Übereinstimmung für den Funktionsaufruf ist eine Funktion aufrufen.The next sample shows that the Microsoft C++ compiler calls a function in the most derived class—even if a conversion is required to match one or more of the parameters—and not call a function in a base class that is a better match for the function call.

// compile with: /clr
using namespace System;
ref struct Base {
   void Test2(Single d) {
      Console::WriteLine("Base::Test2");
   }
};

ref struct Derived : public Base {
   void Test2(Double f) {
      Console::WriteLine("Derived::Test2");
   }
};

int main() {
   Derived ^ t = gcnew Derived;
   // Base::Test2 is a better match, but the compiler
   // calls a function in the derived class if possible
   t->Test2(3.14f);
}

AusgabeOutput

Derived::Test2

Das folgende Beispiel zeigt, dass eine Funktion ausgeblendet werden kann, auch wenn die Basisklasse die gleiche Signatur wie die abgeleitete Klasse hat.The following sample shows that it's possible to hide a function even if the base class has the same signature as the derived class.

// compile with: /clr
using namespace System;
ref struct Base {
   int Test4() {
      Console::WriteLine("Base::Test4");
      return 9;
   }
};

ref struct Derived : public Base {
   char Test4() {
      Console::WriteLine("Derived::Test4");
      return 'a';
   }
};

int main() {
   Derived ^ t = gcnew Derived;

   // Base::Test4 is hidden
   int i = t->Test4();
   Console::WriteLine(i);
}

AusgabeOutput

Derived::Test4
97

KopierkonstruktorenCopy constructors

Der C++-Standard besagt, dass ein Kopierkonstruktor aufgerufen wird, wenn ein Objekt so verschoben wird, dass ein Objekt an derselben Adresse erstellt und zerstört wird.The C++ standard says that a copy constructor is called when an object is moved, such that an object is created and destroyed at the same address.

Jedoch wenn "/ CLR" wird zum Kompilieren und eine Funktion, die kompiliert wird, um MSIL-Aufrufe, die Funktion eine Native Funktion, bei der eine – oder mehr als ein – übergebener Wert und, in denen die native Klasse hat, einen Kopierkonstruktor und/oder der Destruktor, der keine Kopie Konstruktor wird aufgerufen, und das Objekt zerstört wird, auf eine andere Adresse als die, in dem es erstellt wurde.However, when /clr is used to compile and a function that's compiled to MSIL calls a native function where a native class—or more than one—is passed by value and where the native class has a copy constructor and/or destructor, no copy constructor is called and the object is destroyed at a different address than where it was created. Dies kann Probleme verursachen, wenn die Klasse einen Zeiger zu sich selbst hat oder wenn der Code Objekte nach Adresse erfasst.This could cause problems if the class has a pointer into itself, or if the code is tracking objects by address.

Weitere Informationen finden Sie unter /clr (Common Language Runtime-Kompilierung).For more information, see /clr (Common Language Runtime Compilation).

Das folgende Beispiel veranschaulicht, was passiert, wenn ein Kopierkonstruktor nicht generiert wird.The following sample demonstrates when a copy constructor is not generated.

// compile with: /clr
#include<stdio.h>

struct S {
   int i;
   static int n;

   S() : i(n++) {
      printf_s("S object %d being constructed, this=%p\n", i, this);
   }

   S(S const& rhs) : i(n++) {
      printf_s("S object %d being copy constructed from S object "
               "%d, this=%p\n", i, rhs.i, this);
   }

   ~S() {
      printf_s("S object %d being destroyed, this=%p\n", i, this);
   }
};

int S::n = 0;

#pragma managed(push,off)
void f(S s1, S s2) {
   printf_s("in function f\n");
}
#pragma managed(pop)

int main() {
   S s;
   S t;
   f(s,t);
}

AusgabeOutput

S object 0 being constructed, this=0018F378
S object 1 being constructed, this=0018F37C
S object 2 being copy constructed from S object 1, this=0018F380
S object 3 being copy constructed from S object 0, this=0018F384
S object 4 being copy constructed from S object 2, this=0018F2E4
S object 2 being destroyed, this=0018F380
S object 5 being copy constructed from S object 3, this=0018F2E0
S object 3 being destroyed, this=0018F384
in function f
S object 5 being destroyed, this=0018F2E0
S object 4 being destroyed, this=0018F2E4
S object 1 being destroyed, this=0018F37C
S object 0 being destroyed, this=0018F378

Destruktoren und FinalizerDestructors and finalizers

Destruktoren in einem Referenztyp führen eine deterministische Bereinigung von Ressourcen aus.Destructors in a reference type perform a deterministic clean-up of resources. Finalizer bereinigen nicht verwaltete Ressourcen und können deterministisch vom Destruktor oder nicht deterministisch vom Garbage Collector aufgerufen werden.Finalizers clean up unmanaged resources and can be called deterministically by the destructor or nondeterministically by the garbage collector. Weitere Informationen zu Destruktoren in Standard-c++ finden Sie unter Destruktoren.For information about destructors in standard C++, see Destructors.

class classname {
   ~classname() {}   // destructor
   ! classname() {}   // finalizer
};

Das Verhalten von Destruktoren in einer verwalteten Visual C++-Klasse unterscheidet sich von Managed Extensions für C++.The behavior of destructors in a managed Visual C++ class differs from Managed Extensions for C++. Weitere Informationen zu dieser Änderung finden Sie unter Änderungen in der Destruktorsemantik.For more information about this change, see Changes in Destructor Semantics.

Der CLR-Garbage Collector löscht nicht verwendete verwaltete Objekte und gibt ihren Speicher frei, wenn sie nicht mehr benötigt werden.The CLR garbage collector deletes unused managed objects and releases their memory when they are no longer required. Allerdings verwendet ein Typ möglicherweise Ressourcen, die der Garbage Collector nicht freigeben kann.However, a type may use resources that the garbage collector does not know how to release. Diese Ressourcen werden als nicht verwaltete Ressourcen bezeichnet (z. B. systemeigene Dateihandles).These resources are known as unmanaged resources (native file handles, for example). Es wird empfohlen, dass Sie alle nicht verwalteten Ressourcen im Finalizer freigeben.We recommend that you release all unmanaged resources in the finalizer. Da verwaltete Ressourcen nicht deterministisch vom Garbage Collector freigegeben werden, ist es nicht sicher, in einem Finalizer auf verwaltete Ressourcen zu verweisen, da der Garbage Collector diese verwaltete Ressource möglicherweise bereits bereinigt hat.Because managed resources are released nondeterministically by the garbage collector, it's not safe to refer to managed resources in a finalizer because it's possible that the garbage collector has already cleaned up that managed resource.

Ein Visual C++-Finalizer ist nicht mit der Finalize-Methode identisch.A Visual C++ finalizer is not the same as the Finalize method. (In der CLR-Dokumentation werden Finalizer und die Finalize-Methode synonym verwendet).(CLR documentation uses finalizer and the Finalize method synonymously). Die Finalize-Methode wird vom Garbage Collector aufgerufen, der jeden Finalizer in einer Klassenvererbungskette aufruft.The Finalize method is called by the garbage collector, which invokes each finalizer in a class inheritance chain. Im Gegensatz zu Visual C++-Destruktoren führt ein Aufruf des abgeleiteten Klassenfinalizer nicht dazu, dass der Compiler den Finalizer in allen Basisklassen aufruft.Unlike Visual C++ destructors, a derived-class finalizer call does not cause the compiler to invoke the finalizer in all base classes.

Da Microsoft C++ -Compiler unterstützt die deterministische Freigabe von Ressourcen, versuchen Sie nicht, implementieren die Dispose oder Finalize Methoden.Because the Microsoft C++ compiler supports deterministic release of resources, don't try to implement the Dispose or Finalize methods. Wenn Sie jedoch mit diesen Methoden vertraut sind, finden Sie hier ein Beispiel, wie ein Visual C++-Finalizer und ein Destruktor, der den Finalizer aufruft, zum Dispose-Muster zugeordnet werden:However, if you're familiar with these methods, here's how a Visual C++ finalizer and a destructor that calls the finalizer map to the Dispose pattern:

// Visual C++ code
ref class T {
   ~T() { this->!T(); }   // destructor calls finalizer
   !T() {}   // finalizer
};

// equivalent to the Dispose pattern
void Dispose(bool disposing) {
   if (disposing) {
      ~T();
   } else {
      !T();
   }
}

Ein verwalteter Typ verwendet möglicherweise auch verwaltete Ressourcen, die Sie lieber deterministisch freigeben möchten, anstatt sie zu einem bestimmten Zeitpunkt, nachdem das Objekt nicht mehr benötigt wird, vom Garbage Collector nicht deterministisch freigeben zu lassen.A managed type may also use managed resources that you would prefer to release deterministically, and not leave to the garbage collector to release nondeterministically at some point after the object is no longer required. Durch die deterministische Freigabe von Ressourcen kann die Leistung erheblich gesteigert werden.The deterministic release of resources can significantly improve performance.

Microsoft C++ Compiler ermöglicht die Definition eines Destruktors, um Objekte deterministisch zu bereinigen.The Microsoft C++ compiler enables the definition of a destructor to deterministically clean up objects. Verwenden Sie den Destruktor, um alle Ressourcen freizugeben, die Sie deterministisch freigeben möchten.Use the destructor to release all resources that you want to deterministically release. Wenn ein Finalizer vorhanden ist, rufen Sie ihn im Destruktor auf, um Codeduplikate zu vermeiden.If a finalizer is present, call it from the destructor, to avoid code duplication.

// compile with: /clr /c
ref struct A {
   // destructor cleans up all resources
   ~A() {
      // clean up code to release managed resource
      // ...
      // to avoid code duplication,
      // call finalizer to release unmanaged resources
      this->!A();
   }

   // finalizer cleans up unmanaged resources
   // destructor or garbage collector will
   // clean up managed resources
   !A() {
      // clean up code to release unmanaged resources
      // ...
   }
};

Wenn der Code, der den Typ verwendet, nicht den Destruktor aufruft, gibt der Garbage Collector schließlich alle verwalteten Ressourcen frei.If the code that consumes your type does not call the destructor, the garbage collector eventually releases all managed resources.

Das Vorhandensein eines Destruktors bedeutet nicht, dass ein Finalizer vorhanden ist.The presence of a destructor does not imply the presence of a finalizer. Allerdings bedeutet das Vorhandensein eines Finalizers, dass Sie einen Destruktor definieren und den Finalizer von diesem Destruktor aufrufen müssen.However, the presence of a finalizer implies that you must define a destructor and call the finalizer from that destructor. Dies berücksichtigt die deterministische Freigabe von nicht verwalteten Ressourcen.This provides for the deterministic release of unmanaged resources.

Durch Aufrufen des Destruktors wird der Abschluss des Objekts mithilfe von SuppressFinalize unterdrückt.Calling the destructor suppresses—by using SuppressFinalize—finalization of the object. Wenn der Destruktor nicht aufgerufen wird, wird der Finalizer des Typs schließlich vom Garbage Collector aufgerufen.If the destructor is not called, your type's finalizer will eventually be called by the garbage collector.

Die deterministische Bereinigung der Objektressourcen durch Aufrufen des Destruktors kann zur Leistungssteigerung beitragen im Gegensatz zum nicht deterministischen Abschluss des Objekts durch die CLR.Deterministically cleaning up your object's resources by calling the destructor can improve performance compared with letting the CLR nondeterministically finalize the object.

Code, die in Visual C++ geschrieben und kompiliert mit "/ CLR" Destruktor des Typs ausgeführt wird, wenn:Code that's written in Visual C++ and compiled by using /clr runs a type's destructor if:

  • Ein Objekt, das mithilfe von Stapelsemantik erstellt wird, liegt außerhalb des gültigen Bereichs.An object that's created by using stack semantics goes out of scope. Weitere Informationen finden Sie unter C++-Stapelsemantik für Referenztypen.For more information, see C++ Stack Semantics for Reference Types.

  • Während der Erstellung des Objekts wird eine Ausnahme ausgelöst.An exception is thrown during the object's construction.

  • Das Objekt ist ein Member in einem Objekt, dessen Destruktor ausgeführt wird.The object is a member in an object whose destructor is running.

  • Rufen Sie die löschen Operator auf ein Handle (Handle für Objekt (^)).You call the delete operator on a handle (Handle to Object Operator (^)).

  • Sie rufen den Destruktor explizit auf.You explicitly call the destructor.

Wenn der Typ von einem Client verwendet wird, der in einer anderen Sprache geschrieben wurde, wird der Destruktor wie folgt aufgerufen:If your type is being consumed by a client that's written in another language, the destructor is called as follows:

  • Bei einem Aufruf von Dispose.On a call to Dispose.

  • Bei einem Aufruf von Dispose(void) für den Typ.On a call to Dispose(void) on the type.

  • Wenn der Typ in einer C#-using-Anweisung außerhalb des gültigen Bereichs liegt.If the type goes out of scope in a C# using statement.

Wenn Sie ein Objekt eines Referenztyps auf dem verwalteten Heap (nicht mit Stapelsemantik für Referenztypen) erstellen, verwenden Sie Try-finally- Syntax, um sicherzustellen, dass eine Ausnahme den Destruktor Ausführung zu verhindern, dass nicht.If you create an object of a reference type on the managed heap (not using stack semantics for reference types), use try-finally syntax to ensure that an exception doesn't prevent the destructor from running.

// compile with: /clr
ref struct A {
   ~A() {}
};

int main() {
   A ^ MyA = gcnew A;
   try {
      // use MyA
   }
   finally {
      delete MyA;
   }
}

Wenn der Typ einen Destruktor enthält, generiert der Compiler eine Dispose-Methode, die IDisposable implementiert.If your type has a destructor, the compiler generates a Dispose method that implements IDisposable. Wenn ein Typ, der in Visual C++ geschrieben wurde, einen Destruktor enthält, der von einer anderen Sprache verwendet wird, führt der Aufruf von IDisposable::Dispose für diesen Typ dazu, dass der Destruktor des Typs aufgerufen wird.If a type that's written in Visual C++ and has a destructor that's consumed from another language, calling IDisposable::Dispose on that type causes the type's destructor to be called. Wenn der Typ in einem Visual C++-Client genutzt wird, können Sie Dispose nicht direkt aufrufen. Rufen Sie stattdessen den Destruktor mithilfe des delete-Operators auf.When the type is consumed from a Visual C++ client, you can't directly call Dispose; instead, call the destructor by using the delete operator.

Wenn der Typ einen Finalizer enthält, generiert der Compiler eine Finalize(void)-Methode, die Finalize überschreibt.If your type has a finalizer, the compiler generates a Finalize(void) method that overrides Finalize.

Wenn ein Typ entweder einen Finalizer oder einen Destruktor enthält, generiert der Compiler entsprechend dem Entwurfsmuster eine Dispose(bool)-Methode.If a type has either a finalizer or a destructor, the compiler generates a Dispose(bool) method, according to the design pattern. (Weitere Informationen finden Sie unter Dispose-Muster).(For information, see Dispose Pattern). Sie können Dispose(bool) in Visual C++ nicht explizit erstellen oder aufrufen.You cannot explicitly author or call Dispose(bool) in Visual C++.

Wenn ein Typ über eine Basisklasse verfügt, die dem Entwurfsmuster entspricht, werden die Destruktoren für alle Basisklassen aufgerufen, wenn der Destruktor für die abgeleitete Klasse aufgerufen wird.If a type has a base class that conforms to the design pattern, the destructors for all base classes are called when the destructor for the derived class is called. (Wenn der Typ in Visual C++ geschrieben wurde, stellt der Compiler sicher, dass die Typen dieses Muster implementieren.) Dies bedeutet, dass der Destruktor einer Referenzklasse mit seinen Basisklassen und Membern verknüpft wird, wie im C++-Standard festgelegt ist. Zuerst wird der Destruktor der Klasse ausgeführt und dann werden die Destruktoren für die Member in der umgekehrten Reihenfolge ausgeführt, in der sie erstellt wurden. Als Letztes werden die Destruktoren für die Basisklassen in der umgekehrten Reihenfolge ausgeführt, in der sie erstellt wurden.(If your type is written in Visual C++, the compiler ensures that your types implement this pattern.) In other words, the destructor of a reference class chains to its bases and members as specified by the C++ standard—first the class’s destructor is run, then the destructors for its members in the reverse of the order in which they were constructed, and finally the destructors for its base classes in the reverse of the order in which they were constructed.

Destruktoren und Finalizer sind in Werttypen oder Schnittstellen nicht zulässig.Destructors and finalizers are not allowed inside value types or interfaces.

Ein Finalizer kann nur in einem Referenztyp definiert oder deklariert werden.A finalizer can only be defined or declared in a reference type. Wie ein Konstruktor und Destruktor hat ein Finalizer keinen Rückgabetyp.Like a constructor and destructor, a finalizer has no return type.

Nachdem der Finalizer eines Objekts ausgeführt wird, werden die Finalizer in den Basisklassen, beginnend mit dem am wenigsten abgeleiteten Typ, ebenfalls aufgerufen.After an object's finalizer runs, finalizers in any base classes are also called, beginning with the least derived type. Finalizer für Datenmember werden nicht automatisch durch den Finalizer einer Klasse verkettet.Finalizers for data members are not automatically chained to by a class’s finalizer.

Wenn ein Finalizer einen systemeigenen Zeiger in einem verwalteten Typ löscht, müssen Sie sicherstellen, dass Verweise auf oder durch den systemeigenen Zeiger nicht vorzeitig erfasst werden. Rufen Sie den Destruktor auf dem verwalteten Typ auf, anstatt KeepAlive zu verwenden.If a finalizer deletes a native pointer in a managed type, you must ensure that references to or through the native pointer are not prematurely collected; call the destructor on the managed type instead of using KeepAlive.

Zur Kompilierzeit können Sie erkennen, ob ein Typ einen Finalizer oder einen Destruktor enthält.At compile time, you can detect whether a type has a finalizer or a destructor. Weitere Informationen finden Sie unter Compilerunterstützung für Typmerkmale.For more information, see Compiler Support for Type Traits.

Im folgenden Beispiel werden zwei Typen verdeutlicht: ein Typ, der nicht verwaltete Ressourcen hat, und ein Typ mit verwalteten Ressourcen, die deterministisch freigegeben werden.The next sample shows two types, one that has unmanaged resources and one that has managed resources that are deterministically released.

// compile with: /clr
#include <vcclr.h>
#include <stdio.h>
using namespace System;
using namespace System::IO;

ref class SystemFileWriter {
   FileStream ^ file;
   array<Byte> ^ arr;
   int bufLen;

public:
   SystemFileWriter(String ^ name) : file(File::Open(name, FileMode::Append)),
                                     arr(gcnew array<Byte>(1024)) {}

   void Flush() {
      file->Write(arr, 0, bufLen);
      bufLen = 0;
   }

   ~SystemFileWriter() {
      Flush();
      delete file;
   }
};

ref class CRTFileWriter {
   FILE * file;
   array<Byte> ^ arr;
   int bufLen;

   static FILE * getFile(String ^ n) {
      pin_ptr<const wchar_t> name = PtrToStringChars(n);
      FILE * ret = 0;
      _wfopen_s(&ret, name, L"ab");
      return ret;
   }

public:
   CRTFileWriter(String ^ name) : file(getFile(name)), arr(gcnew array<Byte>(1024) ) {}

   void Flush() {
      pin_ptr<Byte> buf = &arr[0];
      fwrite(buf, 1, bufLen, file);
      bufLen = 0;
   }

   ~CRTFileWriter() {
      this->!CRTFileWriter();
   }

   !CRTFileWriter() {
      Flush();
      fclose(file);
   }
};

int main() {
   SystemFileWriter w("systest.txt");
   CRTFileWriter ^ w2 = gcnew CRTFileWriter("crttest.txt");
}

Siehe auchSee also

Klassen und StrukturenClasses and Structs
Klassen und StrukturenClasses and Structs