1. C++이 C보다 편리한 결정적인 요인
C++은 템플릿, 람다 등 온갖 다양한 프로그래밍 패러다임이 추가되어 C보다 훨씬 더 방대하고 복잡한 언어가 되어 있다.
그러나 C++은 맨 처음에 객체지향 언어로 시작했기 때문에 C와의 근본적인 차이는 아무래도 이와 관련된 것들이다.
본인은 C++이 C에 비해서 더 편리하고 간결하게 코딩할 수 있고 사람의 실수를 줄여 주는 제일 강력하고 중요한 요소는 다음 세 가지라고 개인적으로 생각한다.
(1) 클래스 멤버 함수 안에서 this 포인터를 생략하고 바로 자기 멤버를 참조해도 된다.
즉, C의 함수와 비교했을 때, 복잡한 구조체의 포인터인 첫째 인자는 생략 가능하다는 뜻이다. 매번 obj->member 할 필요 없이 바로 member를 쓰면 된다.
(2) 어떤 객체 변수를 선언해 주면(지역/전역) 생성자와 소멸자를 호출하는 코드가 앞뒤에 자동으로 삽입된다.
함수나 블록의 실행이 중간에 끝나더라도(return, break) 메모리를 해제하거나 파일을 닫는 코드를 거치게 하려고 지저분한 goto문을 쓰지 않아도 된다. 예외를 던질 때에도 소멸자 처리가 자동으로 된다는 건 longjmp 따위로 결코 흉내 낼 수 없는 엄청난 축복이다.
(3) 상속이라는 걸 자동으로 제공하고, 포인터 형변환 때의 상위· 하위 상속 관계를 자동으로 맞게 판단해 준다.
파생에서 기반으로 가는 건 괜찮지만, 기반에서 파생으로 가는 건 바로 안 되고 최소한 static_cast라도 해 줘야 된다.
그에 비해 C언어는 void*냐 그렇지 않느냐 하나만 판단하고, void*가 아닌 다른 모든 타입의 포인터들은 서로 남남인 타입일 뿐이다.
2. C++과 Java의 enum class
컴퓨터 프로그램에서는 숫자가 산술 연산의 대상인 수가 아니라 그냥 이산적인 식별 번호로 취급되고, 각각의 값이 서로 완전히 다른 의미를 갖는 경우가 많다. 그래서 프로그래밍 언어에서는 범용적인 정수형뿐만 아니라 sub-range 내지 열거형이란 걸 제공하곤 한다.
sub-range는 파스칼이나 Ada 같은 옛날 언어 유행으로 끝나는 분위기이고, 요즘 대세는 열거형이다.
C언어는 열거형이란 게 있긴 했지만 모종의 이유로 인해 매크로 상수가 훨씬 더 많이 쓰였다. 하긴, 그쪽은 참/거짓 bool 형조차 없었고 그냥 다 int로 퉁쳐서 썼을 정도로 int 만능 덕후 성향이 좀 있었다. =_=
C++에서는 C++11 버전부터 enum class라는 것이 도입됐다. (1) scope을 반드시 지정해 줘야 하고, (2) 정수형으로 암시적으로 형변환이 되지 않아서 type-safety가 강화되니 굉장히 적절한 변화인 것 같다.
즉, 평범한 enum이라면 int를 받는 아무 곳에서나 ENUM_VALUE라고만 써도 됐을 텐데, enum class라면 반드시 static_cast<int>( EnumClass::ENUM_VALUE ) 라고 길게 지정해 줘야 하게 된 것이다. type safety가 강화되었다.
Java에도 enum이 있긴 하지만, 후대인 Java 5에서 추가로 도입된 물건이다. 그렇기 때문에 거기도 상수 명칭을 선언하는 용도로는 재래식 static final int 뭉치가 더 많이 통용돼 왔다.
같이 도입된 건지 또 나중에 추가된 건지는 모르겠지만, Java에도 enum class라는 게 존재한다. 그런데 이건 C++과는 관점이 전혀 다른 재미있는 물건이다.
public enum Planet {
MERCURY (3.303e+23, 2.4397e6),
VENUS (4.869e+24, 6.0518e6),
EARTH (5.976e+24, 6.37814e6),
MARS (6.421e+23, 3.3972e6),
JUPITER (1.9e+27, 7.1492e7),
SATURN (5.688e+26, 6.0268e7),
URANUS (8.686e+25, 2.5559e7),
NEPTUNE (1.024e+26, 2.4746e7);
private final double mass; // in kilograms
private final double radius; // in meters
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}
(... 이후 생략)
}
이렇게, enum {} 내부에는 명칭들을 쭈욱 쓴 뒤, 세미콜론을 찍으면 명칭 나열을 종결할 수 있다.
그 뒤, 다음부터는 클래스를 선언하는 것처럼 public이니 private니 어쩌구 하면서 멤버 함수와 멤버 변수를 쓰면 얘는 enum의 탈을 쓴 평범한 클래스가 된다.
허나, 이 enum 클래스는 new 연산자를 사용해서 임의의 인스턴스를 만들 수 없다. 이 enum의 인스턴스는 저 명칭으로 선언된 녀석들만이 허용된다. 그래서 enum 명칭을 선언과 동시에 저렇게 생성자 함수에다 전할 인자를 ()로 지정할 수 있다. 우와~~~
즉, 일반적으로 enum 명칭들은 0 1 2 3 같은 숫자의 alias에 불과한 반면, enum class는 각각의 명칭들이 이 클래스의 붙박이 인스턴스가 된다는 것이다.
enum은 상수를 나타내는 만큼, enum 클래스는 멤버들도 다들 final로 선언해서 실행 중에 값이 변경되지 않는 속성을 지정하게 하는 편이다.
enum 명칭이 하나밖에 없으면..?? 얘는 자연스럽게 이 클래스의 싱글턴/단일체가 된다. 그렇기 때문에 Java의 enum class는 싱글턴을 만드는 정석 디자인으로 통용되기도 한다.
생성자를 private로 감추는 등 별별 쑈를 해도 serialize나 reflect 같은 꼼수를 통해 싱글턴 객체를 여러 개 만드는 게 가능한 반면, enum class는 언어 차원에서 그런 일이 벌어지지 않는다는 게 보증된다.
정말 신기한 용법이다. C++의 enum class는 클래스처럼 취급되는 enum이지만, Java의 enum class는 enum처럼 생긴 클래스라고 볼 수 있겠다.
3. 이름이 붙지 않은 일회용 함수/클래스
2000년대 이후부터는 C++, C#, Java 같은 주류 프로그래밍 언어에 객체지향뿐만 아니라 함수형이라는 패러다임이 도입되었다. 덕분에 중괄호 {}로 둘러싸인 코드를 통째로 변수에 대입한다거나, 심지어 함수의 인자로 일회용으로 익명으로 전하는 게 가능해졌다. 인자를 받아서 리턴값을 주는 코드의 묶음이지만 굳이 함수의 형태로 선언· 정의하고 이름을 붙일 필요가 없다는 것이다.
심지어는 클래스까지 이렇게 간편하게 선언해서 그 인스턴스를 넘겨줄 수 있다.
Java에서 무슨 이벤트에 대한 handler나 listener를 인자로 넘겨줄 때, new XXXX { } 이러면서 객체 선언과 새 파생 클래스 선언과 주요 함수 오버라이딩을 한번에 하는 것 말이다.
그런데, 이렇게 이름 없는 함수나 이름 없는 클래스는 태생적으로 이름이 필요한 요소를 언어의 문법 차원에서 구현할 수 없다.
람다 함수는 자기 자신을 호출하는 재귀호출을 구현할 수 없다.
그리고 이름 없는 클래스는.. 정말 웃기게도 컴파일러가 기본 생성해 주는 것 말고 자신의 독자적인 생성자와 소멸자를 가질 수 없다. =_=;; 흠..
C++은 Java처럼 저렇게 함수 인자에서 새 파생 클래스를 즉석에서 만드는 것까지 지원하지는 않지만.. 새 클래스를 선언할 때 이름을 생략할 수 있다. 이건 반대로 Java에서 지원하지 않는 문법이다.
이름 없는 클래스나 함수를 만드는 게 가능하니 이름에 의존하지 않고 생성자· 소멸자나 함수 자기 자신을 지칭하는 방법이 있긴 해야 할 텐데.. 이건 그냥 언어 차원에서의 한계로 남겨 두려는가 보다.
참고로 C++은 이름 없는 namespace라는 것도 지원해서 얘는 C의 static의 상위 호환으로 간주하고 있다. 즉, 이 영역에 선언되는 함수나 변수는 다른 번역 단위에서는 인식되지 않는 private한 물건이 된다.
그 밖에 이름 없는 구조체· 공용체도 있는데, 개인적인 생각은 오프셋 보정을 위해 크기(자리)만 차지하는 용도이고 실제로 쓰이지는 않는 멤버에 대해서도 이름을 생략할 수 있었으면 좋겠다.
Posted by 사무엘