Managed Extensions for C++
Managed Extensions for C++ 또는 Managed C++는 C++를 위한 지원이 중단된 언어 확장 세트로, C++ 구문과 언어를 닷넷 프레임워크로 가져오기 위한 문법 및 구문 확장, 키워드, 특성 등을 포함한다. 이 확장 기능은 마이크로소프트가 C++ 코드를 관리 코드 형태의 공통 언어 런타임(CLR)을 대상으로 할 수 있게 하고, 동시에 네이티브 코드와 상호 운용할 수 있도록 하기 위해 만들어졌다.
2004년, Managed C++ 확장은 구문을 명확히 하고 단순화하며 제네릭 프로그래밍을 포함하도록 기능을 확장하는 방향으로 크게 수정되었다. 이러한 새로운 확장은 C++/CLI로 명명되었으며 마이크로소프트 비주얼 스튜디오 2005에 포함되었다. 따라서 Managed C++라는 용어와 그 확장 기능은 지원이 중단되었으며, 새로운 확장 기능으로 대체되었다.
역사
마이크로소프트는 마이크로소프트 비주얼 C++ 2002(MSVC++)에서 Managed Extensions for C++를 도입했다. 마이크로소프트는 표준 C++와 Managed Extensions for C++ 사이의 차이를 최소화하고자 했으며, 결과적으로 두 언어 간의 핵심적인 차이점이 구문상으로 모호해졌다. MSVC++ 2003과 2005 또한 Managed C++로 프로그램을 작성할 수 있도록 지원했다. 2004년, Managed Extensions for C++는 C++를 사용하여 공통 언어 기반을 위한 프로그래밍을 지원하려는 마이크로소프트의 두 번째 시도인 C++/CLI를 위해 지원이 중단되었다.
설계
Managed는 버퍼 오버런 검사와 같은 더 많은 런타임 검사를 통해 향상된 보안을 제공하는 샌드박스 역할을 하는 닷넷 가상 머신에서 실행되거나 이에 의해 관리되는 관리 코드를 의미한다. 또한 Managed C++로 작성된 애플리케이션은 표준 C++ 애플리케이션처럼 네이티브 중앙 처리 장치 명령어로 직접 컴파일되는 것이 아니라 CIL로 컴파일된다.
Managed C++ 코드는 C#이나 비주얼 베이직 닷넷과 같이 CLR을 대상으로 하는 다른 모든 언어와 상호 운용될 수 있으며, 쓰레기 수집과 같이 CLR에서 제공하는 기능을 사용할 수 있다. 이는 Managed C++가 닷넷 언어들 사이에서 독특한 위치를 차지함을 의미한다. Managed C++는 닷넷 언어(C#, VB.NET 등)는 물론 네이티브 C++와도 직접 통신할 수 있는 유일한 언어이다. 다른 닷넷 언어들은 PInvoke나 COM을 통해서만 C++ 코드와 통신할 수 있다. 그러나 Managed C++는 관리 환경과 표준 C++ 환경 모두에서 직접 통신할 수 있기 때문에 종종 "브리지"로 사용된다.
기능
Managed C++로 코딩된 프로그램은 닷넷 프레임워크와 CLR의 추가 기능을 제공한다. 그중 가장 주목할 만한 것은 쓰레기 수집으로, 프로그래머가 수동으로 메모리를 관리할 필요가 없도록 해준다. 쓰레기 수집기(GC)는 CLR에 의해 처리된다. 메모리 관리는 매우 빠르게 실행되지만, 성능이 중요한 애플리케이션의 경우 네이티브 비관리 코드가 더 선호되는 옵션일 가능성이 높다.
Managed C++는 객체 지향 프로그래밍을 지향한다. 표준 C++와 Managed C++의 주요 차이점은 다중 상속이 지원되지 않으며, CLR의 쓰레기 수집기 하에서 관리되는 클래스는 두 개 이상의 클래스를 상속할 수 없다는 점이다. 이는 CLR의 제한 사항 때문이다.
주요 특징:
- 확장 가능한 메타데이터: 관리 구성 요소의 구조와 형식을 설명하기 위해 제공되는 정보이다. 소프트웨어 구성 요소를 만들기 위해 확장 및 재사용될 수 있으며 C#과 비주얼 베이직 닷넷에서 많이 사용된다.
- 쓰레기 수집: CLR은 메모리 관리를 위해 쓰레기 수집기에 의해 완전히 관리된다. 즉, Managed C++ 코드에서 delete 연산자를 호출할 필요가 없다.
- 닷넷 언어와의 상호 운용성: 닷넷 프레임워크를 대상으로 하는 코드는 마이크로소프트 중간 언어(MSIL, 자바 바이트코드와 유사) 출력을 생성한다. 따라서 컴파일된 모듈 및 구성 요소(어셈블리)는 JScript .NET, C#, 비주얼 베이직 닷넷 및 기타 3사 닷넷 언어와 같이 닷넷 프레임워크를 대상으로 하는 다른 언어로 작성된 프로그램 구성 요소에서 재사용될 수 있다.
- 버전 관리: 기존 관리 클래스에 새로운 메서드와 데이터 멤버를 도입해도 기존 클라이언트 측 소프트웨어와의 이진 호환성을 깨뜨리지 않는다.
- 이진 헤더: 미리 컴파일된 메타데이터를 재사용할 수 있게 한다. MSIL로 컴파일된 모든 .exe, .dll, .obj 또는 .netmodule은 C++ 소스 파일에서 참조될 수 있다.
- 버퍼 오버플로 방지: C++에 쓰레기 수집이 도입됨에 따라 Managed C++는 표준 C++의 데이터 형식 검사 부재로 인해 발생하는 일반적인 버퍼 오버플로 오류에 덜 취약하다. 쓰레기 수집기는 이러한 오류의 빈도를 줄이는 데 도움을 준다(완전히 제거하지는 않음).
- 닷넷 프레임워크 기본 클래스 라이브러리: Managed C++는 모든 관리 함수 호출과 상속된 클래스가 닷넷 프레임워크 기본 클래스 라이브러리(BCL, FCL 또는 Framework Class Library라고도 함)에서 파생되므로 표준 비관리 코드보다 덜 장황할 가능성이 있다. 해당 API는 TCP/IP 네트워킹 기능, 텍스트 조작 함수, 데이터 액세스(ODBC에서 SQL까지), XML 서비스(XSD에서 XSL까지), GUI 프로그래밍(윈도우 폼), 메일 서비스(SMTP), 암호화(X509 인증서 및 XML 디지털 서명), MSIL 생성(본질적으로 MSIL 명령어를 방출), 파일 I/O, CLR 쓰레기 수집기의 수동 조작 및 WMI 콘솔을 조작하기 위한 관리 정보를 제공한다.
네이티브 코드 대비 장점
- 관리 코드와 비관리 코드를 동일한 CLI 어셈블리 내에서 매끄럽게 혼합할 수 있다. 이를 통해 프로그래머는 완전히 다시 작성하지 않고는 닷넷 프레임워크로 포팅할 수 없는 비관리 코드를 유지할 수 있다. 다만, 이 하이브리드 규칙을 사용하는 데 따른 몇 가지 영향이 존재한다.
- Managed C++는 비관리 코드를 포함하면서 다른 모든 닷넷 언어와 네이티브 방식으로 통신할 수 있는 유일한 언어이다. 따라서 Managed C++는 닷넷 환경을 사용하는 프로그래머와 표준 C++를 사용하는 프로그래머 사이의 상호 운용성에 매우 편리하다.
비관리 코드 대비 단점
- Managed C++는 코드의 가독성을 저해할 수 있는 많은 새로운 키워드와 구문 규칙을 도입하는데, 특히 C++ 코드가 직접 포함되어 동일한 어셈블리 내의 Managed C++ 코드와 직접 상호 작용하는 경우 더욱 그렇다.
- Managed C++는 C++/CLI에 의해 대체되었으며 C++/CLI가 표준화됨에 따라 구식이 되었다.
완전 관리 코드 대비 단점
- Managed C++는 동일한 결과를 낼 수 있는 다른 닷넷 언어들에 비해 개발 시간이 약간 더 걸린다. Managed C++는 값 형식(__value struct 및 __value class)과 참조 형식(__gc struct 및 __gc class)을 모두 가지고 있기 때문에 포인터 사용이 요구될 수도, 아닐 수도 있다.
- Managed C++는 ASP.NET 웹 애플리케이션을 완전히 지원하지만, 다른 닷넷 언어(일부 타사 언어 포함)에 비해 개발이 더 어렵다.
- Managed C++는 (네이티브 C++와의 상호 운용성을 위한) 템플릿만 지원하며 (다른 모든 닷넷 언어와의 상호 운용성을 위한) 제네릭은 지원하지 않는다. C++/CLI는 템플릿(컴파일 타임)과 제네릭(런타임)을 모두 지원한다.
예제
다음 예제는 표준 C++와 비교하여 Managed C++의 사용을 보여준다.
- (전역 변경) CLR로 포팅할 기존 C++는 다음과 같이 추가되어야 한다.
// Gello.cpp
// 새로운 using 지시문
#using <mscorlib.dll>
// 또 다른 using namespace 지시문
using namespace System;
int main() {
Console::WriteLine("Hello, world!");
return 0;
}
새로운 전처리기 지시문
가 필요하다. 이 외에도 기본 클래스 라이브러리의 더 많은 네임스페이스를 사용하기 위해 더 많은 라이브러리를 가져오는 #using 지시문이 필요하다. 예를 들면
#using <System.Windows.Forms.dll>
와
using namespace System::Windows::Forms;
를 사용하여 윈도우 폼을 사용할 수 있다.
- CLR을 대상으로 코드를 컴파일하려면 새로운 컴파일러 옵션을 도입해야 한다.
cl.exe hello.cpp /clr
/clr은 닷넷 프레임워크를 참조하는 모든 코드가 CIL로 컴파일되도록 한다.
- 클래스는
__gc 확장 키워드를 통해 쓰레기 수집 대상으로 지정할 수 있다.
// GarbageCollected.cpp
#using <mscorlib.dll>
__gc class GarbageCollected {
int* i;
char* g;
float* j;
};
int main() {
while (true) {
GarbageCollected^ _gc = gcnew GarbageCollected();
}
return 0;
}
위의 코드는 메모리 누수에 대한 걱정 없이 컴파일 및 실행될 수 있다. 클래스 gc는 쓰레기 수집기 하에서 관리되기 때문에 delete 연산자를 호출할 필요가 없다. 비관리 코드로 동일한 작업을 수행하려면 delete 키워드가 필요하다:
// NotGarbageCollected.cpp
class NotGarbageCollected {
int* i;
char* g;
float* j;
};
int main() {
while (true) {
NotGarbageCollected* g = new NotGarbageCollected();
delete g;
}
return 0;
}
참고:
- __gc로 지정된 클래스는 생성자를 선언할 수 있다.
- __gc로 지정된 클래스는 소멸자를 선언할 수 있다.
- __gc로 지정된 클래스는 하나 이상의 클래스를 상속할 수 없다. (이는 CLR의 제한 사항이다)
- __gc로 지정된 클래스는 __gc로 지정되지 않은 다른 클래스를 상속할 수 없다.
- __gc로 지정된 클래스는 __gc로 지정되지 않은 다른 클래스에 의해 상속될 수 없다.
- __gc로 지정된 클래스는 원하는 만큼의 __gc 인터페이스를 구현할 수 있다.
- __gc로 지정된 클래스는 비관리 인터페이스를 구현할 수 없다.
- __gc로 지정된 클래스는 기본적으로 자체 어셈블리 외부에서 볼 수 없다.
public __gc class MyClass {
// ...
};
public 키워드를 사용하여 __gc로 지정된 클래스의 접근을 수정한다.
__gc로 지정된 클래스는 delete 키워드를 사용하여 수동으로 삭제할 수 있지만, 사용자 정의 소멸자가 있는 경우에만 가능하다.
- 인터페이스는 앞에 __gc 확장 키워드를 선언할 수 있다. 예:
// MyInterface.cpp
#using <mscorlib.dll>
__gc __interface MyInterface {
void init();
int common();
}
위 코드는 간단한 DLL 파일을 생성하기 위해 /clr 및 /LD로 컴파일해야 한다.
참고:
- __gc __interface는 데이터 멤버, 정적 멤버, 중첩 클래스 선언 및 접근 지정자를 포함할 수 없다.
- __gc __interface는 다른 __gc __interface 인터페이스 또는 System::Object에서만 상속할 수 있다. System::Object에서의 상속이 기본 동작이다.
- __gc __interface는 선언된 함수 프로토타입의 구현(본문 코드)을 포함할 수 없다.
다른 언어와의 비교
다음은 Managed C++와 개념적으로 유사한 다른 잘 알려진 프로그래밍 언어 간의 주요 차이점과 프로그래밍 표준을 포함한다.
표준 C++
단점
- 네이티브 C++ 코드는 런타임에 더 빠를 수 있다.
- C++는 대상 시스템에 관련 컴파일러 및 관리 런타임 환경의 설치를 요구하지 않는다.
- C++는 제네릭 프로그래밍을 지원한다. 하지만 C++/CLI 최종 릴리스 이전까지 Managed C++ 프로그래머는 제네릭을 사용하기 위한 우회 방법을 찾아야 했다.
- C++는 "const" 키워드와 const 정확성을 지원한다. Java나 C#과 마찬가지로 Managed C++에는 이 기능이 포함되어 있지 않다. 대안으로는 관리 클래스를 불변객체로 만들거나 공용 인터페이스의 세터(set accessor) 접근을 제한하는 것이 있다.
- C++ 코드는 CLR의 제한을 받지 않는다. 예를 들어, CLR은 클래스가 다른 클래스를 비공개 또는 보호로 상속하는 것을 허용하지 않으므로 다음은 컴파일러 오류를 발생시킨다:
public __gc class First {
int i;
};
public __gc class Second: private First {
int h;
i = h;
}; // 오류
public __gc class Third: protected First {
int h;
i = h;
}; // 오류
- Managed C++ __gc 클래스는 두 개 이상의 클래스를 상속할 수 없으므로 다음은 컴파일러 오류를 발생시킨다:
__gc class First {};
__gc class Second {};
__gc class Third: public First, public Second {}; // 오류 발생
장점
- Managed C++는 일반 C++보다 더 높은 수준의 리플렉션을 지원하며, 이는 코드의 기능이나 의도에 따라 일반적으로 훨씬 더 편리하다.
- Managed C++는 다른 3사 언어를 포함하여 다른 모든 닷넷 호환 언어와 상호 운용될 수 있다.
- Managed C++는 쓰레기 수집된다. 표준 C++에서는 메모리 관리 및 할당이 프로그래머의 책임이다.
Java
차이점
- Java 코드를 실행하려면 적절한 가상 머신이 필요하지만, Managed C++ 코드를 실행하려면 적절한 닷넷 프레임워크 구현이 필요하다.
단점
- Java는 소스 코드에 대한 문서를 제공하지만 Managed C++는 그렇지 않다.
- Java 프로그래머가 사용할 수 있는 다른 많은 개발 도구가 있지만, Managed C++는 비주얼 스튜디오 .NET에서만 사용할 수 있다.
장점
- Managed C++는 Java보다 훨씬 쉽게 컴퓨터 시스템의 저수준 인터페이스에 액세스할 수 있다. Java 프로그래머는 호스트 운영 체제의 저수준 서비스를 사용하려면 JNI를 사용해야 한다.
C#
차이점
- C#은 C++와 마찬가지로 포인터를 지원하지만, 이 기능은 기본적으로 꺼져 있다.
단점
- 자바와 마찬가지로 C#은 관리 코드를 다룰 때 구문적으로 더 단순하다.
- C#은 Managed C++와 본질적으로 동일한 결과를 달성할 수 있는데, 이는 모든 구문 및 구조적 규칙이 놀라울 정도로 유사하기 때문이다.
- Managed C++는 CLR 도입으로 인해 강력한 형식의 언어이지만, 동일한 코드베이스에 비관리 컴파일 코드가 도입되면 오류가 발생하기 쉬운 반면, C#은 순수 MSIL이다.
장점
- C#은 컴퓨터 시스템의 저수준에 액세스하려면 닷넷 프레임워크와 제공된 클래스 라이브러리를 사용해야 한다.
- C 또는 C++에서 닷넷 프레임워크로 애플리케이션을 포팅하는 것은 Managed C++를 사용하는 것이 훨씬 쉽다.
- Managed C++를 닷넷 프레임워크 대상으로 컴파일하는 마이크로소프트 비주얼 C++ .NET 컴파일러는 결과 어셈블리에서 훨씬 더 성숙한 명령어 세트를 생성하여 성능을 향상시킨다.