출처 : http://soyoja.com/413

예전에도 이런 글을 쓴 적이 있었는데, 이번에 MSDN 에서 잘 정리된 글을 본 김에 두 언어간의 차이를 좀 더 정리해 볼까 한다.


1. C++ 에서 클래스(class) 와 구조체(struct) 는 거의 동일하다. ( C++ 에서 struct 는 모든 멤버가 public 인 class 라고 생각하면 된다 ).
반면에 C# 은 class 와 struct 의 용도가 다르다. C# 의 struct 는 상속을 지원하지 않으며 명시적 기본생성자를 지원하지 않는다. 

2.  C++ 과 달리 C# 의 배열은 C++ 의 vector 와 유사한, 자체적인 메소드를 제공하는 자료구조 개체이다. 예를 들면 C# 의 배열 자료구조는 자체적으로 Min, Max, Length 등의 라이브러리 함수를 제공한다. 또한, C++ 과 C# 은 배열을 선언하는 방법이 매우 다르다. 

- C++ 의 배열 선언 
char split[3] = { ' ', '\t', '\"' };

- C# 의 배열 선언
char[] split = { ' ', '\t', '\"' };  // 방법1
char[] split = new char[3], char[0] = ' ', char[1] = '\t', char[2] = '\"'; // 방법2
char[] split = new char[3]{' ', '\t', '\"' };  // 방법3

 
3. C++ 의 bool 타입은 int 와 동일하다. 반면에 C# 은 bool 은 true / false 를 갖는 고유한 데이터타입이며 아래와 같은 묵시적 타입캐스팅이 불가능하다.

int start = 0;  
if( start ) data.Add(s);  // 암시적으로 int 형을 bool 형으로 변환할 수 없다.

4. C++ 의 long 은 int 와 마찬가지로 32 비트 자료형이다. C++ 에서 64 비트 자료형을 쓰기 위해서는 long long 을 쓴다. 반면에 C# 의 long 은 64 비트 자료형이다.

5. C++ 과 C# 모두 값에 의한 전달과 참조에 의한 전달을 지원한다. C 와 C# 모두 명시적으로 참조에 의한 전달을 표시하지 않은 경우 기본적으로는 값에 의한 전달방식으로 처리된다. C++ 과 C# 에서의 참조에 의한 전달 방식은 각각 아래와 같다.
 
- C++ 의 방식 


#include <iostream>
using namespace std;
void Method(int &i)
{
    i = 44;
}
int main()
{
    int val = 0;
    Method(val);
 cout << val << endl;  // val = 44
    return 0;
}

 
- C# 의 방식 
class RefExample
{
    static void Method(ref int i)
    {
        i = 44;
    }
    static void Main()
    {
        int val = 0;
        Method(ref val);
        // val is now 44
    }
}

6. C# 은 기본적으로는 포인터 사용이 금지되어 있다. C# 에서 포인터를 쓰기 위해서는 컴파일러 옵션으로 _unsafe 를 쓴다. 포인터 사용을 가급적 금지한 이유는 C/C++ 에게 있어서 강력한 기능인 포인터가 그만큼 잘못사용할 경우 심각한 오류를 일으키는 존재였기 때문이다. 조엘 스폴스키는 최근에 널리 사용되는 프로그래밍 언어(대표적으로 자바를 많이 까더군) 들이 보여주는 포인터 사용에 제한적인 트렌드에 대해서 개탄하면서, 수준낮은 프로그래머들은 절대 포인터에 대해서 이해하지 못한다고 독설을 남기기도 했다. 어쨌든 보다 배우고 쓰기 쉽고, 안정성 추구하는 C# 의 언어 설계철학을 엿볼 수 있다.

7. switch 문
- C++ 과 달리 C# 의 switch 문은 break 가 없는 경우 자동적으로 다음 case 로 넘어가지 않는다. C++의 switch 문에서 break 를 빼 먹어서 생기는 실수를 방지하기 위한 규칙으로 보인다. 단, C# 의 switch ~ case 에서 case 문에 코드가 없는 경우에는 다음 case 로 넘어갈 수 있다.

8. C++ 의 typedef 대신에 C# 에서는 using 을 사용한다.

9. C# 의 지역 변수는 C++ 과 달리 항상 초기화를 해야만 사용할 수 있다. C++ 의 경우 실행시 에러가 나더라도 초기화 하지 않은 지역변수를 일단 쓸 수는 있다. (일부 C++ 코드에서는 일부러 초기화 하지 않은 변수를 사용하는 바람직하지 않은 테크닉이 사용되기도 한다) 그러나 C# 은 초기화하지 않은 지역변수를 사용할 경우 컴파일 에러가 발생한다. 

10. 자료구조에 있어서 모든 타입을 자유롭게 쓸수 있게 하여 재사용성과 효율성을 높이는 기법 -  C++ 의 템플릿(Template) 과 유사한 개념이 C# 에서는 제네릭(Generic) 이라 한다. 단, C# 의 제네릭의 타입정보는 런타임에 적용된다.

11. C# 도 C++ 과 마찬가지로 goto 를 지원한다. 단, C# 은 goto 에 지정된 label 에서 반드시 코드가 있어야 한다.
참고로 Java 에는 goto 가 없다. goto 는 강력한 기능이지만 코드의 가독성과 논리적인 flow 를 흐리게 한다는 점에서 많은 논란이 되어 왔다.

12. C++ 에서 사용하는 #define 을 통한 매크로 정의를 C# 은 할 수 없다. C# 의 define 은 컴파일 옵션에 따른 전처리기 사용에 국한된다.

13. unsigned 데이터 타입  : C/C++ 에 존재하는 unsigned 데이터 타입이 C# 과 자바에서는 없다. 
unsigned 타입을 없앤 이유는 signed 와 unsigned 를 혼용할 경우에 발생될 가능성이 있는 오류 때문으로 추정되는데, 여기에 대해서는 아래 포스팅에 잘 설명되어 있다. 

잘 모르겟거든 unsigned 는 쓰지 말지어다

unsigned 자료형의 이점은 양수 데이터를 표현하기 위한 최대치가 2 배로 늘어난다는 점이다. 최근의 컴퓨팅 환경은 메모리에 대한 제약이 예전에 비해 많이 사라져서 메모리에서 손해를 보더라도 오류를 낼 가능성이 있는 unsigned 타입을 삭제하는 언어 설계철학을 도입한 것으로 보인다.

14. C# 은 C++ 과 달리 전역변수와 전역함수를 쓸 수 없다. 따라서 C++ 프로젝트를 할 때 전역변수를 모아놓고 한곳에 관리해서 쓰는 constant.h 헤더를 사용하는 기법등을 C# 에서는 쓸 수 없다. C# 에서 이와 같이 하려면 해당 변수들을 멤버로 가진 클래스를 사용한다. 예를 들면 Constant.cs 를 만들고 여기에 Constant 클래스를 만드는 식으로..

15. C++ 과 달리 C# 은 비트필드를 지원하지 않는다.

16. C# 은 C++ 에는 없는 foreach 문이란 반복문이 존재한다. foreach 문의 용도는 C++ 에서의 for 와 거의 유사하다. 

17. 가비지 컬렉션
최근에 등장하는 언어들의 주요 특징 중 하나가 가비지 컬렉션을 지원한다는 점이다.
C# 에는 delete 가 없다. 단, C# 도 프로그래머가 명시적으로 new 로 할당된 데이터를 삭제할 수 있다.



새로운 언어를 공부하면서 기존에 널리 사용되던 언어와의 차이점을 비교해보고, 왜 이런 차이점이 생기게 되었는지를 생각해 보면 프로그래밍 언어의 설계철학과 발전 과정을 음미해 볼 수 있는 좋은 공부가 된다.

참고 : C++ 개발자를 위한 C#  http://msdn.microsoft.com/ko-kr/library/yyaad03b(VS.90).aspx


===============================================================================================================
C++과 문법적으로 다른점
 여기서는 C++과 C#의 차이점을 간단히 설명할 것이다. 여기서 다른점 모든 것을 설명하는 것은 아니고 기본 문법 요약 정도로 생각하면 될듯하다. 여기서 설명하는 내용은 C++과 다른점이지 C++ .NET과 다른점이라고 생각하면 안된다.
1. Header 파일이 존재하지 않는다.
C++에서 Class 선언을 Header(.h)에 하고 #include를 사용하여 그 Header 파일을 포함 시킴에 의해 .cpp파일의 Class를 사용하도록 한다. 그러나 C#은 .cs 파일만으로 구성된다. 실제 C++에서 Header파일은 형선언을 하여 그 Class를 사용한다고 컴파일러에게 알려주는 역할을 수행한다. 이 부분을 하기 위해서 Header 파일을 만드는 일도 약간이나마 번거러운 일이다. C#은 같은 프로젝트에 .cs로 존재하거나 다른 어셈블리(.exe, .dll)일 경우 참조(향후 설명)만 하면 자동으로 인식하여 컴파일을 하게된다.
 
2. Namespace라는 개념이 추가 되었다.
Namespace는 Class를 감싸는 대표이름이라고 생각하면 된다. 여러개의 Class를 제작하여 하나의 대표이름을 붙여 놓은 것이 namespace다. 이것은 같은 이름의 Class를 여러개 만들수 있도록 하는데, 같은 이름의 Class가 다른 namespace에 존재하면 구분이 가능하게 된다.
여기서 설명의 편의상 Class라고 했으나 Class는 Type의 대표적 종류이다.
 
Type
- Struct
- Enum
- Class
- Interface
- Delegate
Delegate만 제외하면 모두 C++에도 있는 개념이다. Delegate는 Event와 쌍으로 사용하는데 C# 문법에서 가장 중요한 개념이다. 자세한 설명은 두 번째 강의에서 설명하도록 하겠다.
참고로 Struct와 Class는 C#에서 거의 같은 기능을 수행한다. Struct도 Method(함수), Property(변수)등 모든 Member를 가질 수 있다. 다만 Class는 상속이 가능하고 Struct는 불가능하다는 것만 다르다. 이 두가지의 차이는 만들때의 목적을 생각하면 기억하기 좋다. Struct는 빠른 데이터 처리를 위해 만들어 졌고, Class는 다양한 가지 기능을 가질수 있도록 만들어 졌다. 상속은 속도는 저하되나 다양한 기능을 지원할 수 있도록 한다.
Member
- Field
- Method
- Property
- Event
여기서도 Event만 제외하면 모두 C++에 있는 개념이다.
 
3. using
위에서 설명한 바대로 namespace라는 개념이 추가되어 같은 이름의 Type을 여러개 작성할 수 있도록 되었다. 그러나 이개념에 의해 Method하나 호출하기 위해 Namespace.Type.Method의 형태로 호출해야만한다.
즉 MessageBox하나 호출하기 위해 System.Windows.Forms.MessageBox.Show("Hellow World")라고 호출해야만 하는 번거로움이 있다. 여기서 System.Windows.Forms가 Namespace다(Type 및 Member는 사이에 .을 넣지 않으므로 구분이 쉽게 될것이다). 이 Namespace를 생략하고 쓸수 있는 방법이 있는데 이것이 바로 using이다.
즉 using System.Windows.Forms를 선언하여 컴파일러에게 namespace 사용을 알려 준 후 MessageBox.Show("Hellow World")라고 호출하면 같은 기능을 수행하게 된다.
 
4. delete가 존재하지 않는다.
   C#은 new로 메모리를 할당하는 것은 C++과 같으나 메모리를 삭제하는 delete가 존재하지 않는다. 메모리 삭제는 가비지컬렉터가 사용하지 않는 시점에 알아서 삭제를 해 준다.
 
5. 포인터가 없어졌다.
   C, C++에서 초보자를 괴롭히는 것이 포인터이다. 포인터 교육시 잘사용하는 예제는 다음과 같다.
void Swap(int *data1, int *data2)
{
     int dataBuff;
     dataBuff = *data1;
     *data1 = *data2;
     *data2 = dataBuff;
}
int nValue1 = 100, nValue2 = 200;
Swap(&nValue1, &nValue2);
 
   뭐 다들 알겠지만 Swap에 nValue1과 nValue2의 주소를 넘겨 주어 주소의 내용을 서로 바꾸므로 실제 내용이 nValue1은 200으로 nValue2는 100으로 바뀌어 들어가게 된다.
   C++에서는 다음과 같이 레퍼런스 개념이 생겼다.
void Swap(int &data1, int &data2)
{
     int dataBuff;
     dataBuff = data1;
     data1 = data2;
     data2 = dataBuff;
}
int nValue1 = 100, nValue2 = 200;
Swap(nValue1, nValue2);
 
   레퍼런스를 사용함으로 해서 포인터를 사용한것과 같이 Swap 기능을 구현한 것이다. 좀 보기가 편해진 감이 있다.
   C#에서는 레퍼런스만 사용할 수 있는데 사용 또한 다음과 같이 바뀌었다.
void Swap(ref int data1, ref int data2)
{
     int dataBuff;
     dataBuff = data1;
     data1 = data2;
     data2 = dataBuff;
}
int nValue1 = 100, nValue2 = 200;
Swap(ref nValue1, ref nValue2);
 
   ref 대신 in(입력 전용), out(출력 전용)을 사용할 수 있다.
 
6. 기본적으로 sizeof를 사용할 수 없다.
  C나 C++에서 sizeof는 많이 사용하는 기능이다. 그러나 C#에서는 sizeof가 내부적으로 메모리를 직접 다루는 명령어 이므로 권장하지 않는다. sizeof는 구조체 복사와 파일에서부터 읽을 때 그 구조체의 크기를 구할 때 가장 많이 사용한다. 예를 들어 다음과 같은 경우이다.
typedef struct Size
{
     int cx;
     int cy;
}
Size size1(10, 20), size2;
memcpy(&size2, &size1, sizeof(Size));
 
 이것은 C#에서 단순히 =을 사용하면 복사가 이루어 진다.
struct Size
{
     public int cx;
     public int cy;
}
Size size1(10, 20), size2;
size2 = size1;
 
  또한 파일로부터 읽고 쓸때는 구조체 Member Method로 Save와 Load를 만들어 개별 Member 변수를 읽을 수 있도록 구성하면 된다.
  참고로 꼭 sizeof를 사용해야 하는 경우라면 sizeof를 사용하는 코드를 unsafe{}로 감싸 주고, 프로젝트 속성에서 안전하지 않은 코드 블록 허용을 True로 설정해야 한다. 그러나 호환성도 떨어지고 안전하지 않으므로 가능하면 사용하지 말기 바란다.
 
7. 데이터 형
   데이터 형은 C#과 C++은 거의 같다. 다른 부분이라면 char, long, decimal이다. char는 하나의 문자를 저장하는 기능을 수행하며 C++과 다르게 2byte를 차지 한다. C++에서는 ASCII 코드를 사용하였으나 C#에서는 유니코드(전세계언어를 2byte코드로 나타낸것)를 사용한다. 참고로 1byte를 차지하는 데이터 형은 byte와 sbyte가 있다. long은 C++에서는 4byte를 차지 했으나 C#에서는 8byte를 차지한다. decimal은 8byte 상수로 추가되었다.
 
8. 배열 선언
   C++에서는 배열을 다음과 같이 설정했을 것이다.
   int arr[30];
   C#에서는 다음과 같이 선언한다.
   int[] arr = new int[30];
 
9. if 조건문
  int a = 1, b = 8;
   if(a && b >= 5)
 
  위 문장은 C++에서 당연히 참이다. 그러나 C#에서는 컴파일 에러가 나타난다. 위 문장은 if(a!=0 && b >= 5)로 수정해야만 한다. 조건문 내부에는 boolen으로 구성되어야 한다. 이것은 C++에서도 마찬가지였다. 그러나 C#에서는 형변환 규칙이 엄격해졌다. 따라서 위 문장에서 a는 boolen변수가 아니므로 에러가 나타나는 것이다. 이것은 다음과 같은 오류를 줄여 준다.
if(a == 1)이라고 해야 하는 자리에 if(a = 1)라고 하면 위와 같은 이유로 C#에서는 컴파일 에러를 내어 준다. 이 실수는 한번쯤은 해봤으리라 예상이 된다.
그러나 C#에서도 a가 boolen으로 선언되었다면 if(a = 1)은 에러가 나타나지 않을 것이다.
 
10. switch 문
switch(a)
{
   case 3:
       b = 7;
   case 4:
       c = 3;
       break;
}
 

  위문장은 C++에서는 이상이 없으나 C#에서는 컴파일 에러가 나타난다. break없이 다음 case로 넘어가는 것을 허용하지 않는다. 예외로 아래와 같이 첫 번째 case 문에 아무것도 하지 않고 다음 case로 넘어 가는 것은 허용한다.
switch(a)
{
   case 3:
   case 4:
       c = 3;
       break;
}
 
 
11. 반복문
int[] arr = new int[30];
for(int i = 0; i < arr.Length; i++)
{
   Console.Write("{0}", arr[i]);
}
 
  위 문장을 실행하면 0 이 30번 출력된다(new를 사용하면 데이터가 0으로 초기화 되는 것을 보장한다). C#에서는 다음과 같이 해도 같은 기능을 수행한다.
int[] arr = new int[30];
foreach(int value in arr)
{
   Console.Write("{0}", value);
}
 
  참고로 Write의 {0}는 첫 번째 인자라는 뜻이다.
 
12. static 메서드
   C++에서는 외부변수와 외부함수 선언(C기능)이 가능했다. 그러나 C#은 완벽한 OOP개념의 언어이므로 이 두가지를 전혀 지원하지 않고 Type 내에 선언해야 한다. Type내에 메서드를 선언하면 호출시 다음과 같이 해야만한다.
class Calc
{
     public double Add(double value1, double value2)
     {
         return value1 + value2;
     }
}
 
   Calc calc = new Calc;
   double result = calc.Add(1, 2);
   그러나 Add 메서드를 호출하기 위하여 무조건 위와 같이 해야 하는 것은 약간 번거롭다. 따라서 Type내의 메서드를 static으로 선언하는 것을 지원한다.
class Calc
{
     public static double Add(double value1, double value2)
     {
         return value1 + value2;
     }
}
 
   double result = Calc.Add(1, 2);
   즉 static 메서드는 Class이름.메서드의 형태로 바로 호출할 수 있게 해준다. 참고로 9번 반복문에서 Console.Write는 Write를 static 메서드로 선언 했기 때문에 호출가능한 구조이다.
 
13. C++ 은 다중 상속이 가능하나 C#은 단일 상속만 가능하다.
  Class Calc1, Calc2, Calc3가 있다고 가정하고 Calc3가 Calc1의 기능과 Calc2의 기능을 모두 사용해야 한다면, C++에서는 class Calc3 : public Calc1, public Calc2 라고 선언하여 가능했다. 그러나 C#에서는 이것을 허용하지 않는다. 굳이 사용하고자 한다면 Calc2를 Calc1으로부터 상속받도록 하고 Calc3는 Calc2로 상속받아 계층적 구조를 이루어야만 한다.
  이것은 어떤면에서 C#의 단점이라고 할 수 있다. 그러나 다중상속은 잘못 사용하면 프로그래머에게 엄청난 오류를 범하게 한다고 한다. 그 오류의 가능성을 사전에 차단했다라고 보면 될듯하다.
 
  기본 문법 설명은 여기까지 하겠다. 나머지는 프로그래밍을 하면서 차츰익히기 바란다.
  여기서 이해가 안된다고 너무 괴로워하지 말기 바란다. 프로그래밍은 모든 문법을 다 외워서 하는 것이 아니다. 사용하면서 저절로 익히는 것이다.


 

'개발지식창고 > C#' 카테고리의 다른 글

C++로 만든 DLL 을 C#에서 사용하기  (0) 2013.01.03
Serialize an ArrayList object to a binary file  (0) 2012.12.24
C# 관련 추천사이트  (0) 2012.12.04
Form 이벤트  (0) 2012.11.30
C++과 C#의 차이  (0) 2012.02.27
Posted by 모과이IT
,