스트롭스트룹 교수님과의 대화: "C++ 배우는데 C를 먼저 배울 필요 없어요"
2013년 여름, C++을 만드신 당시 텍사스 A&M(TAMU) 교수였던1 비야네 스트롭스트룹(Bjarne Stroustrup) 교수님이 내가 일하는 그룹에 오셔서 현대 C++에 대한 강의를 하셨다.2 보통 이런 강의가 끝나면 30분 정도의 1:1 만남을 신청할 수 있다. 언제 또 C++ 창시자님과 대화를 나눠볼까 싶어서 냉큼 신청했고 이야기를 나눴다. 우리팀이 하고 있던 프로젝트 관련 조언을 좀 구한 뒤, 일반적인 질문도 좀 드려봤다. 멍청한 질문은 아닐까 조마조마했다. 주로 C++에서 포인터 사용과 올바른 C++ 학습에 대해 의견을 나눴다. 제목이 말하듯이 C++을 배우는데 더는 C를 거칠 필요가 없다. 짧았지만 이 대화 이후에 보다 다른 시각으로 C++을 볼 수 있게 되었다.
C++에서 C 포인터 사용에 대해 어떻게 생각하세요?
이런 질문을 드려봤다.
“구글의 C++ 코딩 가이드라인을 보면 함수 인자로 포인터 아니면 상수형 참조자만 쓰라고 합니다. 저희 팀 역시 참조자는 잘 쓰지 않고 포인터를 자주 씁니다. 최초의 C++은 C를 발전시켜서 만든 터라 C의 포인터를 그대로 계승하고 있는데요. 혹시 포인터를 좋아하세요? 여전히 참조자가 천대받는 코딩 규칙에 대해 어떻게 생각하세요?”
나의 예상은 포인터를 아마도 싫어 하시겠지만, 영향력 있는 발언을 하시는 분이니 비판을 하더라도 다소 완곡한 표현을 쓰지 않을까였다.
아니었다.
한숨을 내쉬면서 생각보다 굉장한 어조로 포인터를 싫어한다고 말씀하셨다. 가능하다면 스마트 포인터와 참조자를 쓸 것을 권유하셨다. 나 역시 포인터를 너무 C++에서 남용하는 것 같아 싫어했지만, 주요 코딩 규칙들이 포인터를 더 우선시 하니 어쩔 수가 없었다. 그런데, C++ 만드신 분조차 이렇게 확실한 태도로 포인터를 비판하시니 한편으론 위안, 한편으론 호불호를 거침없이 표현하시는 것이 무척 흥미로웠다. 리누스 투르발스의 그 유명한 “엔비디아, f*** you” 정도는 아니었지만, 당시 내 느낌은 상당히 신선했다.
생각해보면 C 스타일의 포인터를 C++에서 그렇게 쓸 필요가 없다. 무엇보다 포인터에 대한 오해가 너무 심하다.
포인터, 그러니까 무엇을 가리키고 참조한다는 개념은 자바나 파이썬 같은 명시적인 포인터가 없는 언어에도 있다. 이걸 모르면 제대로 프로그래밍을 할 수 없다. C/C++가 어려운 이유로 ‘포인터’를 꼽는 것은 잘못되었다. 정확히 말하자면, 가리키는 행위가 참조자 같은 것으로 추상화되지 않았고, 고로 프로그래머가 직접 주소를 얻어 그 값을 다뤄야 하니 어려운 것이다. C 언어는 call-by-reference도 없다. 주소 값을 담고 있는 포인터가 그냥 call-by-value로 불릴 뿐이다. 포인터는 별 이상한 녀석도 아니고 그저 주소 값을 담고 있는 완벽한 변수이다.
C++의 참조자는 이런 포인터의 문제를 - 완벽하지는 않지만 - 조금이라도 쉽게 한다. C++ 참조자는 선언과 함께 반드시 초기화되어야 하며, 그때 정해진 대상은 나중에 바꿀 수 없다. 포인터와 근본적으로 다른 점이다. C++ 참조자는 실제 변수가 아니다. 참조자를 변수처럼 쓰지만, 실제 객체가 아니어서 주소 값을 얻을 수 없다. 가리키는 대상이 포인터처럼 무제한적이지 않아서 (아마도) 최대한 참조자로만 코드를 만들면 컴파일러가 코드 최적화하기 쉬울 수도 있다.
하지만 대부분의 C++ 프로그래머가 C를 먼저 배웠던터라 오히려 C++ 참조자를 헷갈려한다. 그러다 보니 함수 인자에서 상수형 참조자 외에는 쓰지 말라는 규칙이 흔하게 사용된다.
다행히 다른 현대 C++ 프로젝트는 적극적으로 참조자를 쓰고 있다. LLVM은 자유롭게 참조자를 쓴다(물론 여전히 상수형 참조자 + 포인터 조합이 흔하지만). Chromium의 Blink 프로젝트의 포인터와 레퍼런스에 대한 규칙을 보면 참조자의 함수 인자로서의 사용을 금하지는 않는다. 페이스북의 HHVM 역시 참조자 사용 금지 같은 건 없다.
포인터는 정말 메모리 덩어리를 주소로 접근해야 할 때만 쓰면 된다. 동적 할당도 std::make_unique
과 std::make_shared
로부터 얻은 스마트 포인터를 쓰면 포인터 사용을 극단적으로 줄일 수 있다. 구글 코딩 가이드라인 역시 스마트 포인터 사용을 적극 장려하고 있다. C++11에서 도입된 우측값 참조와 무브 시맨틱으로 컴파일 시간에 std::uniqe_ptr
의 잘못된 소유권 전달은 가려낼 수 있다. 더욱더 포인터를 덜 써도 되는 환경이다.
스트롭스트룹 교수님은 그래서 C++에 대한 교육 방법도 바뀌어야 한다고 역설하셨다. 제발 C++을 C의 연장선으로 가르치지 말라고. 재직하던 TAMU에서 학생들을 효과적으로 가르치고자 책까지 새로 썼다고 하셨다. 바로 “Programming: Principles and Practice Using C++”이다. 이 책에서 포인터, 메모리, 주소는 전체 27장 중 17장에서 소개한다.
char*
대신에 std::string
을, 배열보다는 std::vector
를 가르쳐야 한다고 하셨다. 그 당시 오래된 C 코드에서 문자열, 배열, 해시테이블의 부재로 엄청 고생하던 나로서는 진심으로 와 닿는 말씀이었다.
C++를 처음으로 배운다면 C를 먼저 배울 필요가 절대 없어요.
오해하지 말아야 할 것은 C가 필요 없다는 것이 아니다. 여전히 C가 필요한 곳도 많다. 단지 C++을 배울 때 C를 거칠 필요 없이 새로운 언어로 배우라는 것이다. 2015년 cppcon (C++ 컨퍼런스)에 흥미로운 강연이 있었다. 케이트 그레고리라는 분이 “Stop Teaching C”라는 제목으로 한 강연이다(슬라이드, 동영상). 내가 여기서 주장하는 내용과 비슷하다.
C도 아닌 C++를 배우는데 너무 빨리 포인터, char*
기반의 문자열, 동적 배열 등을 가르칠 필요는 없다. 시작은 C++11부터 도입된 여러 기능을 적극 써서 - 거짓말 약간 보태 - 파이썬스럽게 가르쳐도 된다. 포인터나 동적 할당은 좀 뒤에서 배워도 괜찮다.
“어떻게 C/C++을 하면서 포인터, 메모리 할당 등을 안 가르칠 수 있냐!”라고 반박할 수 있다. 맞는 말이다. 특히 C 언어에 한정한다면 더더욱 옳다. 21세기에 C 언어는 기계어와 가장 근접한 언어이니 C 언어에 있어 컴퓨터/시스템 구조 지식은 필수이다. C++도 아주 잘 쓰려면 결국 저수준까지 내려가야 하지만, 나의 주장은 시작부터 괜히 진입 장벽을 높일 필요가 없다는 것이다. 가뜩이나 요즘 같이 웹이 모든 것을 잡아먹은 세상에선 C++ 조차도 너무나 아랫단에 있다. 시야를 아래로만 한정할 필요는 없다.
아직 C++ 입문서 중 C 언어의 확장으로 시작하는 책들이 더러 있다. 심각하게 비판하는 것은 아니지만, 현대 C++도 나왔는데 다른 교습법이 이제 필요할 때가 아닌가 생각한다. Yes24 기준으로 가장 많이 판매되는 C++ 입문서를 보면 서두 부분에 “C++은 C언어를 포함한다”라는 문구가 있다. 엄밀히 말하자면 기술적으로 틀린 말이다. 대부분의 독자가 C를 공부했음을 가정했을테니 보다 쉽게 접근하고자 C 이야기를 꺼낼 수 있지만, 오히려 방해된다. 최근 저술되는 C++ 입문서는 비교적 C와 C++의 차이를 분명히 인지하고 다른 언어로서 접근하는 것 같아 다행이다.
C++: 가볍게 추상화한 언어
C++의 시작은 C에서 시작되었고, C++이 성공한 가장 큰 이유는 분명히 C와의 호환성 때문이다. 하지만 지금은 - 아니 이미 오래전부터 - 서로 다른 길을 가고 있다.
그렇다면 도대체 C++를 어떻게 봐야 할까? 흔히 C++을 객체지향 언어, 멀티패러다임 언어라고 부르는데 장님 코끼리 만지듯 일부만 짚은 표현이다.
정답은 스트롭스트룹 교수님의 강연 도중 찾을 수 있었다. 아래와 같이 명쾌하게 정의하였다.
“A light-weight abstraction programming language”
“가볍게 추상화한 프로그래밍 언어”3
“가볍다”라는 점에서 C와 아주 흡사하지만 “추상화”라는 관점에선 C++이 C와 크게 다르다. 특히 현대 C++에 추가된 기능은 이 철학을 충실히 지키므로 더욱 멋진 정리이다. C++을 배울 때도, 가르칠 때도, 이 철학을 상기하면 좋지 않을까 생각한다.
. . . . . . . . .
시간이 다 될 무렵, “The C++ Programming Language” 책에 사인을 받으면서 마지막으로 짧은 질문을 드렸다.
“왜 쓰신 책의 코딩 폰트가 고정 폭이 아니라 가변 폭으로 하셨어요?”
“아, 그건 가변 폭으로 하니 한 줄에 더 많이 쓸 수 있어서 그렇게 했어요.”