게임 개발에 사용하는 대표적인 엔진인 언리얼 엔진과 유니티 엔진은 각각 C++, C# 언어로 스크립트를 작성한다. 두 언어 모두 C언어에서 파생되었다는 것과 객체지향 프로그래밍 언어라는 공통점이 있지만, 꽤나 큰 차이가 있다.
· 컴파일 결과
C++와 C#은 똑같이 컴파일이라는 과정을 거쳐 실행 파일을 생성하게 되지만, 프로그래머가 작성한 소스파일의 컴파일 결과로 생성되는 중간 파일의 형태가 다르다.
C++로 작성한 코드는 어플리케이션의 실행 파일을 운영체제가 바로 실행하기 때문에, 컴퓨터가 이해할 수 있는 기계어에 가까운 어셈블리어라는 언어로 컴파일한다.
반대로 C#으로 작성한 어플리케이션은 운영체제 위의 .NET이라는 가상 머신 실행 환경에서 실행되기 때문에 .NET에 적합한 IL(Intermidiate Language)라는 중간수준의 언어로 컴파일한다. IL로 컴파일된 C# 스크립트는 .NET 가상 머신의 구성 요소인 CLR(Common Language Runtime)에 의해 실행된다.
이로 인해 발생하는 현상은 두 가지이다.
- 첫 번째로 기본적으로 C#으로 프로그래밍할 땐 .NET이라는 실행 환경이 기기에 갖춰져 있어야 하기 때문에 .NET이 없는 시스템에선 어플리케이션이 실행 불가능하다는 것이다. 하지만 현대 대부분의 플랫폼에서 .NET을 지원해 큰 문제가 되진 않는다.
- 두 번째는 고수준 언어에서 저수준 언어로 컴파일하는 C++의 컴파일 시간이 고수준 언어에서 중간수준 언어로 컴파일하는 C#의 컴파일 시간보다 매우 길다는 것이다. 대표적인 예시로 유니티에서 C# 스크립트를 수정하고 유니티 에디터에 복귀했을 때 즉시 컴파일 결과가 반영되는 JIT(Just In Time) 컴파일이 가능한 것에 반해 언리얼의 스크립트 컴파일 방식인 핫 리로딩 혹은 라이브 코딩은 꽤 오랜 시간이 소요된다.
· 값 타입 / 참조 타입
데이터를 값 타입과 참조 타입으로 구분한다고 했을 때도 두 언어 사이의 차이가 발생한다.
- 값 타입C#의 값 타입은 기본 타입과 구조체만이 값 타입으로 분류된다.
- 이때 C#의 구조체를 조금 알고 가면 좋은데, C++의 구조체는 기본 접근지시자가 public인 클래스라고 봐도 무방한 개념이지만, C#은 2개 이상의 기본 타입을 캡슐화해 새로운 데이터 타입을 정의하기 위한 도구라고 볼 수 있다. 따라서 필드를 활용한 함수 등을 구조체 내부에 선언할 순 있지만, 상속 등의 기능이 제한된다. 구조체에 선언하는 함수는 기능적인 역할을 최소화하여 데이터 캡슐화 자체에만 집중하는 것이 좋다.
- C++에서의 값 타입은 정적으로 할당한 모든 형태의 데이터이다. 해당 데이터가 int, double, char 등의 기본 타입이든, 클래스를 이용해 만든 객체이든 정적으로 할당했다면 값 타입으로 분류된다.
- 참조 타입C#의 참조 타입은 클래스로 만든 객체들을 참조 타입으로 분류한다.
- C++에서의 참조 타입은 동적으로 할당한 모든 형태의 데이터와 & 기호를 붙여 선언한 참조형 변수가 참조 타입으로 분류된다.
· 동적 할당
위에서 살펴본 C++과 C#의 값 / 참조 타입 분류는 동적 할당과 연관이 있다. 기본적으로 C++에서 모든 데이터 타입을 포인터 변수로 선언할 수 있고, 그에 따른 동적 할당이 가능하다. 하지만 C#은 포인터 문법을 지원하지 않아(개념은 존재한다) 참조 타입 데이터에 대해서만 동적 할당을 해준다. 즉, C#에서 기본 타입이나 구조체는 동적 할당이 불가능하고 클래스만 동적 할당하여 정의한다.
만약 Test 클래스 객체를 선언은 하였는데 동적 할당으로 초기화를 해주지 않으면 C++은 자동으로 Default constructor를 호출하지만 C#은 에러를 일으킨다. 이러한 초기화 문제는 비단 참조 타입의 문제가 아니라 값 타입도 동일하게 적용된다. C#은 초기화에 굉장히 민감한 언어라는 것에 유의하자.
· 메모리 해제
동적으로 할당한 메모리는 필요가 없어졌을 때 메모리를 해제해주어야 한다. C++는 new 연산자로 동적 할당한 메모리를 delete 연산자로 직접 해제해주어야 하지만, C#은 CLR이 GC(Garbage Collector)라는 메커니즘을 활용해 자동으로 메모리를 해제해준다.
물론 Modern C++에서 스마트 포인터라는 개념이 등장함으로써 자동으로 동적 할당된 메모리를 해제해준다. 따라서 스마트 포인터의 메모리 해제 타이밍도 명확하게 알 수 있다. 하지만 C#의 GC는 현재 메모리 상태에 따라서 CLR이 판단해 자동으로 해주기 때문에 다소 다른 방식이라고 할 수 있다. 이것이 반드시 좋은 것도 아닌 게, Garbage Collection 작업도 오버헤드가 있어 시스템이 바쁘게 돌아가고 있을 때 GC가 실행되면 시스템에 장애를 일으킬 수 있는데, 이를 프로그래머가 완벽하게 제어할 수 없어 종종 문제가 생길 수 있다는 것이다.
· 문법
1. C++에선 int 타입으로도 bool 타입과 같은 참거짓 판단이 가능했지만, C#은 bool 타입으로만 가능하다.
2. C++에선 switch case문의 case 레이블에 문자열을 사용할 수 없었지만, C#은 가능하다. 또한, C++에선 특정 case 레이블에서 작업을 수행한 뒤 break로 빠져나오지 않으면 자동으로 다음 레이블로 이동하지만, C#에선 이것이 불가능하다.
3. 범위 기반 for문을 Modern C++에선
for(auto ele : array){ ... }
와 같이 작성했지만, C#에선
foreach(int ele in array){ ... }
라는 별개의 함수로 작성한다.
'프로그래밍 언어' 카테고리의 다른 글
[C/C++] 주석으로 함수 매개변수 설명 추가하는 방법 (0) | 2023.12.13 |
---|---|
C, C++, C# VB 데이터 타입 및 비교표 (0) | 2023.08.08 |
프로그램 언어의 종류 Low Level (저급), High Level (고급) (0) | 2023.07.26 |