방법: 클래스 및 구조체 정의 및 사용 (C++/CLI)How to: Define and Consume Classes and Structs (C++/CLI)

이 문서에서는 정의 및 사용자 정의 참조 형식 및 C + 값 형식을 사용 하는 방법을 보여 줍니다. + CLI입니다.This article shows how to define and consume user-defined reference types and value types in C++/CLI.

목차Contents

개체 인스턴스화Object instantiation

암시적 추상 클래스Implicitly abstract classes

형식 표시 유형Type visibility

멤버 표시 유형Member visibility

Public 및 private 네이티브 클래스Public and private native classes

정적 생성자Static constructors

이 의미 체계 포인터Semantics of the this pointer

서명으로 숨기기 함수Hide-by-signature functions

복사 생성자Copy constructors

소멸자 및 종료자Destructors and finalizers

개체 인스턴스화Object instantiation

관리 되는 힙에서 하지 스택이나 네이티브 힙에 참조 (ref) 형식만 인스턴스화할 수 있습니다.Reference (ref) types can only be instantiated on the managed heap, not on the stack or on the native heap. 값 형식은 스택이나 관리 되는 힙 인스턴스화할 수 있습니다.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;
}

암시적 추상 클래스Implicitly abstract classes

암시적 추상 클래스 인스턴스화할 수 없습니다.An implicitly abstract class can't be instantiated. 클래스의 기본 형식이 인터페이스이며 클래스가 인터페이스의 모든 멤버 함수를 구현하지 않는 경우 클래스는 암시적 추상입니다.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.

인터페이스에서 파생된 클래스의 개체를 만들 수 없는 경우 해당 이유는 클래스가 암시적 추상이기 때문입니다.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. 추상 클래스에 대 한 자세한 내용은 참조 하세요. 추상합니다.For more information about abstract classes, see abstract.

다음 코드 예제는 MyClass 함수가 구현되지 않아서 MyClass::func2 클래스를 인스턴스화할 수 없음을 보여 줍니다.The following code example demonstrates that the MyClass class cannot be instantiated because function MyClass::func2 is not implemented. 해당 예제를 컴파일하려면 MyClass::func2의 주석 처리를 제거하십시오.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.
}

형식 표시 유형Type visibility

어셈블리가 참조된 경우 어셈블리의 형식이 어셈블리 외부에서 표시되거나 표시되지 않도록 CLR(공용 언어 런타임)의 표시 유형을 제어할 수 있습니다.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 형식을 포함 하는 소스 파일에 표시 되는지 나타냅니다는 #using 형식을 포함 하는 어셈블리에 대 한 지시문입니다.public indicates that a type is visible to any source file that contains a #using directive for the assembly that contains the type. private 형식이 포함 된 소스 파일에 표시 되지 않음을 나타냅니다는 #using 형식을 포함 하는 어셈블리에 대 한 지시문입니다.private indicates that a type is not visible to source files that contain a #using directive for the assembly that contains the type. 그러나 private 형식은 동일한 어셈블리 내에는 표시됩니다.However, private types are visible within the same assembly. 기본적으로 클래스에 대한 표시 유형은 private입니다.By default, the visibility for a class is private.

Visual Studio 2005 이전 기본적으로 네이티브 형식에는 어셈블리 외부에 public 액세스 가능성이 있었습니다.By default prior to Visual Studio 2005, native types had public accessibility outside the assembly. 사용 하도록 설정 컴파일러 경고 (수준 1) C4692 참조는 private 네이티브 형식이 잘못 사용할 수 있도록 합니다.Enable Compiler Warning (level 1) C4692 to help you see where private native types are used incorrectly. 사용 된 make_public pragma를 수정할 수 없는 소스 코드 파일에서 네이티브 형식에 public 액세스 가능성을 제공 합니다.Use the make_public pragma to give public accessibility to a native type in a source code file that you can't modify.

자세한 내용은 #using 지시문을 참조하세요.For more information, see #using Directive.

다음 샘플은 형식을 선언하고 해당 액세스 가능성을 지정한 다음 어셈블리 내부에서 이러한 형식에 액세스하는 방법을 보여 줍니다.The following sample shows how to declare types and specify their accessibility, and then access those types inside the assembly. 물론 #using을 사용하여 private 형식의 어셈블리를 참조하는 경우 어셈블리 내에서 public 형식만 표시됩니다.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();
}

출력Output

in Public_Class
in Private_Class
in Private_Class_2

이제 DLL로 빌드되도록 이전 샘플을 다시 작성합니다.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");}
};

다음 샘플은 어셈블리 외부에서 형식에 액세스하는 방법을 보여 줍니다.The next sample shows how to access types outside the assembly. 이 샘플에서 클라이언트는 이전 샘플에서 빌드한 구성 요소를 사용합니다.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;
}

출력Output

in Public_Class

멤버 표시 유형Member visibility

public, protectedprivate 액세스 지정자 쌍을 사용하여 어셈블리 외부에서 public 클래스의 멤버에 액세스하는 것과 다르게 동일한 어셈블리 내에서 public 클래스의 멤버에 액세스할 수 있습니다.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

이 표에서는 다양한 액세스 지정자의 효과를 요약하여 보여 줍니다.This table summarizes the effect of the various access specifiers:

지정자Specifier 효과Effect
publicpublic 멤버는 어셈블리 내부 및 외부에서 액세스할 수 있습니다.Member is accessible inside and outside the assembly. 참조 공용 자세한 내용은 합니다.See public for more information.
privateprivate 멤버는 어셈블리 내부 및 외부 모두에서 액세스할 수 없습니다.Member is not accessible, neither inside nor outside the assembly. 참조 개인 자세한 내용은 합니다.See private for more information.
protectedprotected 멤버는 파생된 형식에 한해 어셈블리 내부 및 외부에서 액세스할 수 있습니다.Member is accessible inside and outside the assembly, but only to derived types. 참조 보호 자세한 내용은 합니다.See protected for more information.
internalinternal 멤버가는 어셈블리 내부에서 public 이지만 어셈블리 외부에서는 private입니다.Member is public inside the assembly but private outside the assembly. internal는 상황에 맞는 키워드입니다.internal is a context-sensitive keyword. 자세한 내용은 상황에 맞는 키워드합니다.For more information, see Context-Sensitive Keywords.
공용 보호 또는 보호 된 공용public protected -or- protected public 멤버는 어셈블리 내부에서 public이지만 어셈블리 외부에서는 protected입니다.Member is public inside the assembly but protected outside the assembly.
개인 보호 또는 보호 된 개인private protected -or- protected private 멤버는 어셈블리 내부에서 protected이지만 어셈블리 외부에서는 private입니다.Member is protected inside the assembly but private outside the assembly.

다음 샘플은 다른 액세스 가능성으로 선언된 멤버가 포함된 public 형식을 보여 준 다음 어셈블리 내부에서 해당 멤버의 액세스를 보여 줍니다.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();
}

출력Output

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
=======================

이제 DLL로 이전 샘플을 빌드합니다.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("=======================");
   }
};

다음 샘플은 이전 샘플에서 만들어진 구성 요소를 사용하므로 어셈블리 외부에서 멤버에 액세스하는 방법을 보여 줍니다.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();
}

출력Output

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

Public 및 private 네이티브 클래스Public and private native classes

네이티브 형식은 관리되는 형식에서 참조될 수 있습니다.A native type can be referenced from a managed type. 예를 들어 관리되는 형식의 함수는 네이티브 구조체 형식의 매개 변수를 사용할 수 있습니다.For example, a function in a managed type can take a parameter whose type is a native struct. 관리되는 형식과 함수가 어셈블리 내에서 public이면 네이티브 형식도 public이어야 합니다.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;
};

다음으로 네이티브 형식을 사용하는 소스 코드 파일을 만듭니다.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) {}
};

이제 클라이언트를 컴파일합니다.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);
}

정적 생성자Static constructors

클래스나 구조체 같은 CLR 형식은 정적 데이터 멤버를 초기화하는 데 사용할 수 있는 정적 생성자를 보유할 수 있습니다.A CLR type—for example, a class or struct—can have a static constructor that can be used to initialize static data members. 정적 생성자는 한 번만 호출되며 형식의 정적 멤버가 처음으로 액세스하기 전에 호출됩니다.A static constructor is called at most once, and is called before any static member of the type is accessed the first time.

인스턴스 생성자는 항상 정적 생성자가 실행된 후 실행됩니다.An instance constructor always runs after a static constructor.

컴파일러는 클래스에 정적 생성자가 있는 경우 생성자에 대한 호출을 인라인할 수 없습니다.The compiler cannot inline a call to a constructor if the class has a static constructor. 컴파일러는 클래스가 값 형식이고, 클래스에 정적 생성자는 있지만 인스턴스 생성자가 없는 경우 멤버 함수에 대한 호출을 인라인할 수 없습니다.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. CLR은 호출을 인라인할 수 있지만 컴파일러는 인라인할 수 없습니다.The CLR may inline the call, but the compiler cannot.

CLR에 의해서만 호출될 수 있도록 정적 생성자를 private 멤버 함수로 정의합니다.Define a static constructor as a private member function, because it is meant to be called only by the CLR.

정적 생성자에 대 한 자세한 내용은 참조 하세요. 방법: 인터페이스 정적 생성자 정의 (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();
}

출력Output

in static constructor
10
11

이 의미 체계 포인터Semantics of the this pointer

Visual C++를 사용하여 형식을 정의하는 경우 참조 형식에서 this 포인터는 "핸들" 형식입니다.When you are using Visual C++ to define types, the this pointer in a reference type is of type "handle". 값 형식에서 this 포인터는 "내부 포인터" 형식입니다.The this pointer in a value type is of type "interior pointer".

이러한 서로 다른 의미 체계의 this 포인터는 기본 인덱서가 호출될 때 예기치 않은 동작이 발생할 수 있습니다.These different semantics of the this pointer can cause unexpected behavior when a default indexer is called. 다음 예제에서는 참조 형식과 값 형식 모두에서 기본 인덱서에 액세스하는 올바른 방법을 보여 줍니다.The next example shows the correct way to access a default indexer in both a ref type and a value type.

자세한 내용은 다음 항목을 참조하세요.For 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();
}

출력Output

10.89
10.89

서명으로 숨기기 함수Hide-by-signature functions

표준 C++에서 기본 클래스의 함수는 파생 클래스 함수에 동일하지 않은 매개 변수의 수나 종류가 있는 경우에도 파생 클래스에서 동일한 이름을 가진 함수에 의해 숨겨져 있습니다.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. 이 이라고 이름순 숨기기 의미 합니다.This is referred to as hide-by-name semantics. 참조 형식에서 기본 클래스의 함수는 이름 및 매개 변수의 목록이 모두 동일한 경우 파생 클래스의 함수에 의해서만 숨겨질 수 있습니다.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. 이 이라고 서명으로 숨기기 의미 합니다.This is known as hide-by-signature semantics.

클래스의 모든 함수가 메타데이터에 hidebysig로 표시되면 클래스는 hide-by-signature 클래스라고 간주됩니다.A class is considered a hide-by-signature class when all of its functions are marked in the metadata as hidebysig. 기본적으로 아래에 생성 된 모든 클래스 /clrhidebysig 함수입니다.By default, all classes that are created under /clr have hidebysig functions. 클래스에 hidebysig 함수가 있는 경우 컴파일러는 함수를 직접 기본 클래스의 이름으로 숨기지 않습니다. 하지만 컴파일러에서 상속 체인의 hide-by-name 클래스를 발견하는 경우 hide-by-name 동작이 계속됩니다.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.

hide-by-signature 의미 체계에 따라 함수가 객체에 호출되면 컴파일러는 함수 호출을 충족할 수 있는 함수가 포함된 가장 많이 파생된 클래스를 식별합니다.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. 호출을 충족할 수 있는 함수가 클래스에 하나 뿐인 경우 컴파일러는 해당 함수를 호출합니다.If there is only one function in the class that could satisfy the call, the compiler calls that function. 호출을 충족할 수 있는 함수가 클래스에 둘 이상이 있는 경우 컴파일러는 오버로드 확인 규칙을 사용하여 호출할 함수를 결정합니다.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. 오버 로드 규칙에 대 한 자세한 내용은 참조 하세요. 함수 오버 로드합니다.For more information about overload rules, see Function Overloading.

지정된 함수 호출의 경우 기본 클래스의 함수는 파생 클래스의 함수보다 조금 더 일치하는 시그니처를 가지고 있을 수 있습니다.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. 그러나 함수가 파생 클래스의 객체에 명시적으로 호출된 경우 파생 클래스의 함수가 호출됩니다.However, if the function was explicitly called on an object of the derived class, the function in the derived class is called.

반환 값이 함수 시그니처의 일부분으로 간주되지 않으므로 반환 값의 형식이 다른 경우에도 기본 클래스 함수가 파생 클래스 함수와 이름이 동일하며 인수의 수와 종류가 동일한 경우 기본 클래스 함수가 숨겨집니다.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.

다음 샘플은 기본 클래스의 함수가 파생 클래스의 함수에 의해 숨겨지지 않음을 보여 줍니다.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();
}

출력Output

Base::Test

다음 샘플에 따르면 Microsoft C++ 가장 많이 파생 된 클래스의 함수를 호출 하는 컴파일러-변환 매개 변수 중 하나 이상과 일치 해야 하는 경우에, 함수 호출에 대 한 더 나은 일치 하는 기본 클래스의 함수를 호출 하지.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);
}

출력Output

Derived::Test2

다음 샘플은 기본 클래스에 파생 클래스와 동일한 시그니처가 있는 경우에도 함수를 숨길 수 있음을 보여 줍니다.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);
}

출력Output

Derived::Test4
97

복사 생성자Copy constructors

C++ 표준에 따르면 복사 생성자는 개체가 동일한 주소에서 생성되고 제거되는 것처럼 개체가 이동하면 호출됩니다.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.

그러나 때 /clr 컴파일 및 MSIL 호출 있는 네이티브 클래스 네이티브 함수에 컴파일된 함수에는-하나 이상의-값 및 복사 생성자 및/또는 소멸자, 복사본이 없는 네이티브 클래스에 의해 전달 됩니다 생성자가 호출 하 고 개체와 다른 주소를 생성 된 위치에서 제거 됩니다.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. 클래스 자체에 대한 포인터가 있거나 코드가 주소로 개체를 추적하는 경우 문제가 발생할 수 있습니다.This could cause problems if the class has a pointer into itself, or if the code is tracking objects by address.

자세한 내용은 /clr(공용 언어 런타임 컴파일)을 참조하세요.For more information, see /clr (Common Language Runtime Compilation).

다음 샘플은 복사 생성자가 생성되지 않는 경우를 보여 줍니다.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);
}

출력Output

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

소멸자 및 종료자Destructors and finalizers

참조 형식에서 소멸자는 리소스의 명확한 정리를 수행합니다.Destructors in a reference type perform a deterministic clean-up of resources. 종료자는 관리되지 않는 리소스를 정리하고 소멸자에 의해 명확하게 호출되거나 가비지 수집기에 의해 불명확하게 호출될 수 있습니다.Finalizers clean up unmanaged resources and can be called deterministically by the destructor or nondeterministically by the garbage collector. 표준에서 소멸자에 대 한 내용은 C++를 참조 하세요 소멸자.For information about destructors in standard C++, see Destructors.

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

관리되는 Visual C++ 클래스의 소멸자와 Managed Extensions for C++의 소멸자는 동작이 다릅니다.The behavior of destructors in a managed Visual C++ class differs from Managed Extensions for C++. 이 변경에 대 한 자세한 내용은 참조 하세요. 소멸자 의미의 변경 내용합니다.For more information about this change, see Changes in Destructor Semantics.

CLR 가비지 수집기는 사용하지 않은 관리되는 개체를 삭제하고 더 이상 필요하지 않은 경우 메모리를 해제합니다.The CLR garbage collector deletes unused managed objects and releases their memory when they are no longer required. 그러나 형식에서 가비지 수집기가 해제하는 방법을 알지 못하는 리소스를 사용할 수도 있습니다.However, a type may use resources that the garbage collector does not know how to release. 이러한 리소스를 관리되지 않는 리소스(예: 네이티브 파일 핸들)라고 합니다.These resources are known as unmanaged resources (native file handles, for example). 종료자에서 관리되지 않는 리소스는 모두 해제하는 것이 좋습니다.We recommend that you release all unmanaged resources in the finalizer. 관리되는 리소스는 가비지 수집기에 의해 불명확하게 해제되므로 종료자에서 관리되는 리소스가 안전하다고 할 수 없습니다. 이는 가비지 수집기가 이미 관리되는 리소스를 정리했을 수도 있기 때문입니다.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.

Visual C++ 종료자와 Finalize 메서드는 같지 않습니다.A Visual C++ finalizer is not the same as the Finalize method. CLR 문서는 종료자와 Finalize 메서드를 같은 뜻으로 사용합니다.(CLR documentation uses finalizer and the Finalize method synonymously). Finalize 메서드는 클래스 상속 체인의 각 종료자를 호출하는 가비지 수집기에 의해 호출됩니다.The Finalize method is called by the garbage collector, which invokes each finalizer in a class inheritance chain. Visual C++ 소멸자와 달리 파생 클래스 종료자가 호출되면 컴파일러는 모든 기본 클래스의 종료자를 호출하지 않습니다.Unlike Visual C++ destructors, a derived-class finalizer call does not cause the compiler to invoke the finalizer in all base classes.

때문에 Microsoft C++ 컴파일러는 리소스의 명확한 해제를 지원, 구현 하지 마십시오는 DisposeFinalize 메서드.Because the Microsoft C++ compiler supports deterministic release of resources, don't try to implement the Dispose or Finalize methods. 그러나 이러한 메서드에 익숙한 경우 Visual C++ 종료자 및 소멸자가 Dispose 패턴에 매핑하도록 종료자를 호출하는 방법은 다음과 같습니다.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();
   }
}

관리되는 형식은 명확하게 해제하려는 관리되는 리소스도 사용할 수 있으며 개체가 더 이상 필요하지 않게 된 후 어느 시점에 가비지 수집기가 불명확하게 해제하도록 허용하지 않습니다.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. 리소스의 명확한 해제는 성능을 현저하게 개선할 수 있습니다.The deterministic release of resources can significantly improve performance.

Microsoft C++ 컴파일러 개체를 명확 하 게 정리 하는 소멸자의 정의 사용 하도록 설정 합니다.The Microsoft C++ compiler enables the definition of a destructor to deterministically clean up objects. 소멸자를 사용하여 명확하게 해제하려는 리소스를 모두 해제하십시오.Use the destructor to release all resources that you want to deterministically release. 종료자가 존재하는 경우 코드의 중복을 피하기 위해 소멸자에서 호출하십시오.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
      // ...
   }
};

형식을 사용하는 코드가 소멸자를 호출하지 않으면 가비지 수집기는 결국 관리되는 리소스를 모두 해제합니다.If the code that consumes your type does not call the destructor, the garbage collector eventually releases all managed resources.

소멸자의 존재가 종료자의 존재를 의미하지는 않습니다.The presence of a destructor does not imply the presence of a finalizer. 그러나 종료자의 존재는 소멸자를 정의해야 하고 해당 소멸자에서 종료자를 호출해야 함을 의미합니다.However, the presence of a finalizer implies that you must define a destructor and call the finalizer from that destructor. 이는 관리되지 않은 리소스의 명확한 해제를 제공합니다.This provides for the deterministic release of unmanaged resources.

SuppressFinalize를 통해 소멸자를 호출하여 개체의 종료를 막습니다.Calling the destructor suppresses—by using SuppressFinalize—finalization of the object. 소멸자가 호출되지 않으면 해당 형식의 종료자가 결국 가비지 수집기에 의해 호출됩니다.If the destructor is not called, your type's finalizer will eventually be called by the garbage collector.

소멸자를 호출하여 개체의 리소스를 명확하게 정리하면 CLR이 불명확하게 개체를 종료하는 것에 비해 성능을 향상시킬 수 있습니다.Deterministically cleaning up your object's resources by calling the destructor can improve performance compared with letting the CLR nondeterministically finalize the object.

시각적 개체에 작성 된 코드는 C++ 를 사용 하 여 컴파일된 /clr 경우 형식의 소멸자를 실행 합니다.Code that's written in Visual C++ and compiled by using /clr runs a type's destructor if:

다른 언어로 작성된 클라이언트에 의해 형식이 사용 중인 경우 소멸자가 다음과 같이 호출됩니다.If your type is being consumed by a client that's written in another language, the destructor is called as follows:

  • Dispose에 대한 호출인 경우On a call to Dispose.

  • 형식의 Dispose(void)에 대한 호출인 경우On a call to Dispose(void) on the type.

  • 형식이 C#의 using 문의 범위를 벗어나는 경우If the type goes out of scope in a C# using statement.

(참조 형식에 대 한 스택 의미 체계 사용 하지 않음) 관리 되는 힙에서 참조 형식의 개체를 만드는 경우 사용할 try-finally 구문 예외가 소멸자가 실행 되지 않도록 방지 하지는 되도록 합니다.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;
   }
}

형식에 소멸자가 있는 경우 컴파일러는 Dispose을 구현하는 IDisposable 메서드를 생성합니다.If your type has a destructor, the compiler generates a Dispose method that implements IDisposable. Visual C++로 작성되고 다른 언어에서 사용된 소멸자가 있는 형식의 경우 해당 형식에서 IDisposable::Dispose를 호출하면 해당 형식의 소멸자가 호출됩니다.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. 형식이 Visual C++ 클라이언트에서 사용되면 Dispose를 직접 호출할 수 없습니다. 대신 delete 연산자를 사용하여 소멸자를 호출합니다.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.

형식에 종료자가 있는 경우 컴파일러는 Finalize(void)를 재정의하는 Finalize 메서드를 생성합니다.If your type has a finalizer, the compiler generates a Finalize(void) method that overrides Finalize.

형식에 종료자 또는 소멸자가 있는 경우 컴파일러는 디자인 패턴에 따라 Dispose(bool) 메서드를 생성합니다.If a type has either a finalizer or a destructor, the compiler generates a Dispose(bool) method, according to the design pattern. (내용은 Dispose 패턴).(For information, see Dispose Pattern). Visual C++에서 Dispose(bool)를 명시적으로 작성하거나 호출할 수 없습니다.You cannot explicitly author or call Dispose(bool) in Visual C++.

형식에 디자인 패턴을 따르는 기본 클래스가 있는 경우 파생 클래스의 소멸자가 호출될 때 모든 기본 클래스의 소멸자가 호출됩니다.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. Visual C++에서 형식이 작성된 경우 컴파일러는 형식이 이 패턴을 구현했는지 확인합니다. 다시 말해서 참조 클래스의 소멸자는 C++ 표준에 의해 지정된 기본 항목 및 멤버에 연결됩니다. 먼저 클래스의 소멸자가 실행되면 그 후에 생성된 순서의 반대로 해당 멤버의 소멸자가 실행됩니다. 마지막으로 해당 기본 클래스의 소멸자가 생성된 순서의 반대로 실행됩니다.(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.

소멸자 및 종료자는 값 형식 또는 인터페이스 내부에서 허용되지 않습니다.Destructors and finalizers are not allowed inside value types or interfaces.

종료자는 참조 형식에서만 정의되거나 선언될 수 있습니다.A finalizer can only be defined or declared in a reference type. 생성자와 소멸자와 같이 종료자는 반환 형식이 없습니다.Like a constructor and destructor, a finalizer has no return type.

개체의 종료자가 실행된 후 기본 클래스의 종료자도 최소 파생된 형식으로 시작하여 호출됩니다.After an object's finalizer runs, finalizers in any base classes are also called, beginning with the least derived type. 데이터 멤버의 종료자는 클래스의 종료자에 의해 자동으로 연결되지 않습니다.Finalizers for data members are not automatically chained to by a class’s finalizer.

종료자가 관리되는 형식에서 네이티브 포인터를 삭제한 경우에는 네이티브 포인터에 대한 또는 네이티브 포인터를 통한 참조가 이미 수집되었는지 확인하고 KeepAlive를 사용하는 대신 관리되는 형식에서 소멸자를 호출합니다.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.

컴파일 타임에서 형식에 종료자 또는 소멸자가 있는지 확인할 수 있습니다.At compile time, you can detect whether a type has a finalizer or a destructor. 자세한 내용은 형식 특성에 대 한 컴파일러 지원합니다.For more information, see Compiler Support for Type Traits.

다음 샘플은 관리되지 않은 리소스가 있는 형식과 명확하게 해제된 관리되는 리소스가 있는 형식을 보여 줍니다.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");
}

참고자료See also

클래스 및 구조체(C++)Classes and Structs
클래스 및 구조체(C++)Classes and Structs