title: "**C++/CLI: .NET 프레임워크 프로그래밍을 위한 가장 강력한 언어(2/2)**"
description: "**C++/CLI: .NET 프레임워크 프로그래밍을 위한 가장 강력한 언어(2/2)**"
cleanUrl: /sw-engineer/intro-cplusplus-cli-2
ogImage: ""
floatFirstTOC: right

C++/CLI: .NET 프레임워크 프로그래밍을 위한 가장 강력한 언어(1/2)에서 이어지는 글입니다.

<aside> 💡 본 글은 MSDN에 올라온 "C++: The Most Powerful Language for .NET Framework Programming"의 번역문입니다.

</aside>

타입 되돌아보기

박싱(boxing)에 대해 썰을 풀기 전에, 값 타입과 참조 타입을 왜 구분하는지 되짚어보는 곳도 괜찮을 거야.

값 타입의 인스턴스는 단순한 값으로 여기고, 참조 타입의 인스턴스는 객체로 여길 수도 있지. 객체의 필드를 저장하는 데는 메모리가 필요할 뿐만이 아니라, 모든 객체에는 객체 헤더가 있는데, 이 헤더는 가상 메소드를 위한 클래스 계층도 등의 객체 지향 프로그래밍의 기본 서비스가 가능토록 하고, 모든 종류의 용도에 붙게될 메타데이터를 제공토록 하지. 하지만, 가상 메소드와 인터페이스로 인한 이 객체 헤더의 메모리 부하는, 종종 너무 값비싼일이 될 수도 있는데, 특히 원하는 전부가 정적 타입인 단순 값과 그 값에 대한 몇몇 컴파일러에 강제된 연산일 경우에 그래. 물론 몇몇 경우에는 컴파일러가 이 객체 부하를 옵티마이징으로 제거할 수도 있지만, 모든 경우에 해당하는 것은 아니지. 적어도 성능(performance)에 대해 신경쓴다면, 분명 managed 코드에는 값과 값 타입을 다루는 데에 이점이 있어. 여기에는 네이티브 C++의 타입 시스템에서와 같은 빈틈(split)이 없단 말야. 물론 C++는 어떤 프로그래밍 패러다임도 부과하지 않지만, 이러한 이유로 인하여 C++위에 라이브러리를 만들어냄으로써 독특한 타입 시스템을 구축하는 것이 가능해지지.

박싱(boxing)

그럼 박싱이란 무엇일까? 박싱이란 값과 객체간의 빈틈을 연결짓는 메카니즘이야. 비록 CLR은 모든 타입이, 간접적이건 직접적이건 간에, **Object**에서 파생될 것을 요구하지만, 사실 값에 대해서는 그렇지가 않아. 스택에 존재할 정수와 같은 단순 값은 단지 컴파일러가 특정 연산을 가능케 하는 하나의 메모리 블록일 뿐이야. 만약 값을 객체처럼 다루고 싶다면, 그 값은 반드시 객체가 되어야 해. 그 값은 **Object**에서 파생된 메소드를 제공할 수 있어야 하지. 박싱이란 개념은 바로 이를 가능케 하기 위해 CLR이 제공하는 메카니즘이야. 그러므로 박싱이 실제로 어떻게 동작하는지를 알아두는 것은 꽤나 유용한 일이쥐. 첫째, 값은 ldloc IL 명령어에 의해 스택에 담겨. 둘째, 이 박싱 IL 명령어를 사용하는 데는 큰 부하가 걸려. 컴파일러는 **Int32**같은 그 값의 정적 타입을 제공하고, CLR은 계속하여 그 값을 스택에서 빼내온(pop) 다음, 그 값과 그 값에 대한 객체 헤더를 담을 충분한 양의 메모리를 할당하지. 이 새로이 생성된 객체에 대한 참조(reference)는 스택에 담겨(push). 이 과정 모두가 그 박싱 명령어에 의한 결과야. 마지막으로, 그 객체에 대한 참조를 얻기 위해서는 stloc IL 명령어를 이용하여 스택에서 그 참조를 빼내서 지역 변수에 저장해야 되.이제 질문은, 값에 대한 박싱을 프로그래밍 언어가 명시적 연산으로 표현하는지, 또는 묵시적 연산으로 표현하는지에 대한 것이야. 다른 말로 표현하자면, 이를 위해 명시적 캐스트, 또는 어떤 다른 구조물이 사용되는가란 뜻이야. C# 언어 설계자는 묵시적 변환을 선택했어. 결국, 정수는 **Object**에서 직접적으로 파생되는 Int32 타입이야.

int i = 123;
object o = i;

우리가 이미 배웠다시피, 문제는 박싱이 단순한 업캐스트(upcast)가 아니라는 데 있어. 그보다는 박싱이란 잠재적으로 값비싼 연산인, 값에서 객체로의 변환이야. 이러한 이유로, MC++에서는 __box 키워드를 이용하여 박싱을 명시적으로 행하지.

int i = 123;
Object* o = __box(i);

물론 MC++에서는 값을 박싱할 때에 정적 타입 정보를 버리지 않아도 돼. 하지만 C#에는 이러한 기능이 없지.

int i = 123;
int __gc* o = __box(i);

강력히 타입에 묶여 박싱된(strongly-typed boxed) 값에는 **dynamic_cast**를 사용하지 않고 단순히 그 객체를 역참조함으로써 값 타입으로의 재변환(unboxing)을 이룰 수 있다는 이점이 있지.

int c = *o;

물론 MC++에서의 명시적 변환으로 인한 구문적(syntactic) 부하는 대부분의 경우에서 너무 크다고 증명되었어. 이러한 이유로, C++/CLI 언어 설계 중 이 부분이 변경되어, 묵시적으로 변환하는 C#의 그것으로 바뀌었지. 이와 동시에, C++/CLI는 다른 .NET 언어가 표현할 수 없는 강력히 타입에 묶여 박싱된(strongly-typed boxed) 값을 직접적으로 표현하는 타입 안정성(type-safety)을 간직했어.

int i = 123;
int^ hi = i;
int c = *hi;
hi = nullptr;

물론 이 것이 암시하는 바는 객체를 가리키지 않는 핸들을 **0**으로 초기화할 수 없다는 것인데(포인터와는 달리), 왜냐하면 이렇게 하면 단순히 값 **0**를 박싱하는 결과를 가져오걸랑. 바로 이것이 nullptr 상수이 존재하는 이유야. 이 상수는 어떤 핸들에건 대입될 수 있어, C#의 null 키워드와 동일한 역할을 하지. 비록 **nullptr**가 C++/CLI 언어에 새로이 도입된 키워드이긴 하지만, Herb Sutter와 Bjarne Stroustrup이 표준 C++에서조차 포인터에 사용하라고 추천하는 놈이기도 해.