본문 바로가기

Programming/C & C++

EC++ Study_2

Chapter1. C++에 왔으면 C++의 법을 따르자.

  • 항목 1: C++을 언어들의 연합체로 바라보는 안목은 필수

    • 초창기의 C++은 단순히 C 언어에 객체 지향 기능 몇 가지가 결합된 형태였다. 오죽하면 C++의 처음 이름조차도 이 점을 드러내려는 듯 '클래스를 쓰는 C(C with Classes)'였을까.
    • C++은 그 후 꾸준한 성장을 거쳤다. 기능, 아이디어, 프로그래밍 전략들을 취해 자기 것으로 만드는 데 있어 점점 대담하고 과감한 행보를 보였다. 이렇게 발전한 오늘날의 C++은 다중패러다임 프로그래밍 언어(multiparadigm programming language)라고 불린다. 절차적(procedural) 프로그래밍을 기본으로 해 객체 지향(object-oriented), 함수식(functional), 일반화(generic) 프로그래밍을 포함해 메타프로그래밍(metapro-gramming) 개념까지 지원하고 있다. 이런 엄청난 표현력과 유연성 덕택에 C++은 소프트웨어 개발에 있어 대체할 만한 것이 없는 도구가 되었지만, 혹자에게는 어느 정도 혼동을 줄 여지가 있는 것도 사실이다. 그렇다면 C++은 어떻게 이해해야 잘했다고 소문이 날까?
    • 가장 쉬우면서 정확한 방법은 C++을 단일 언어로 바라보는 눈을 넓혀, 상관관계가 있는 여러 언어들의 연합체(federation)로 보아라. 그리고 나서 각 언어에 관한 규칙을 각개 격파하는 것이다. 이렇게 해 가면 시각이 단순해지고 명확해지며, 기억하기도 편하다. 물론, 규칙은 언어마다 정도의 차이는 있겠으나 다를 수 있다. C++을 제대로 따라잡으려면, 이 언어가 여러 개의 하위 언어(sublanguage)를 제공한다는 점을 새기고 있어야 한다. 하위 언어는 다음 4가지이다.

    1) C : C++을 처음 배우는 사람조차도 알고 있는 불변의 사실이 이것이다. C++은 여전히 C를 기본으로 하고 있다. 블록, 문장, 선행 처리자, 기본제공 데이터타입, 배열, 포인터 등 모든 것이 C에서 왔다.

    2) 객체 지향 개념의 C++ : 이 부분에 '클래스를 쓰는 C'에 관한 것이 모두 해당된다. 클래스(생성자와 소멸자 개념까지), 캡슐화, 상속, 다형성, 가상 함수(동적 바인딩) 등이다. 학교에서 배웠던 객체 지향 설계의 규칙드 ㄹ대부분이 그대로 들어맞는 부분이라고 보면 된다.

    3) 템플릿 C++ : C++의 일반화 프로그래밍 부분으로, 많은 프로그래머들이 경험해 보지 않은 영역 중 하나이다. 템플릿의 주체 못할 강력함이 너무나도 크고 훌륭해서 완전히 새로운 프로그래밍 패러다임이 파생되기까지 했다. 이름하여 템플릿 메타프로그래밍(template metaprogramming: TMP)이다.

    4) STL : STL은 이름에서 알 수 있듯이 템플릿 라이브러리이다. 하지만 대단히 특별한 템플릿 라이브러리라고 말할 수 있다. STL의 세계는 컨테이너(container), 반복자(iterator), 알고리즘(algorithm)과 함수 객체(function object)가 만수산 드렁칡처럼 얽혀 돌아가는 것을 규약으로 삼고 있으나, 템플릿과 라이브러리는 얼마든지 다른 아이디어를 중심으로 만들어질 수 있다. 또한, STL은 나름대로 독특한 사용규약이 있어서 STL을 써서 프로그래밍하려면 그 규약을 따르면 된다.

  • C++은 한 가지 프로그래밍 규칙 아래 똘똘 뭉친 통합 언어(unified language)가 아니라 네 가지 하위 언어들의 연합체이다. 각각의 하위 언어가 자신만의 규칙을 가지고 있다. "하위 언어들로 구성되어 있다"라는 점을 꼭 새겨두어라. 일단 이렇게만 하면 C++ 이해의 관문에 들어서기가 대단히 쉬워질 것이다.
  • 항목 2: #define을 쓰려거든 const, enum, inline을 떠올리자

    • 단순한 상수를 쓸 때는, #define보다 const 객체 혹은 enum을 우선 생각하자.

    • 함수처럼 쓰이는 매크로를 만드려면, #define 매크로보다 인라인 함수를 우선 생각하자.

  • 항목 3: 낌새만 보이면 const를 들이대 보자

    • const 키워드가 표의 왼쪽에 있으면 *포인터가 가리키는 대상이 상수인 밤년, const가 표의 오른쪽에 있는 경우엔 *포인터 자체가 상수이다. const가 *표의 양쪽에 다 있으면 포인터가 가리키는 대상 및 포인터가 다 상수라는 뜻이다.


    char Example[] = "example";
    char *p = Example; // 비상수 포인터 & 비상수 데이터
    const char *p = Example; // 비상수 포인터 & 상수 데이터
    char * const p = Example; // 상수 포인터 & 비상수 데이터
    const char * const p = Example; // 상수 포인터 & 상수 데이터


    • STL 반복자(iterator)는 포인터를 본뜬 것이기 때문에, 기본적인 동작 원리가 T* 포인터와 진짜 흡사하다. 어떤 반복자를 const로 선언하는 일은 포인터를 상수로 선언하는 것(즉, T* const 포인터)과 같다. 반복자는 자신이 가리키는 대상이 아닌 것을 가리키는 경우가 허용되지 않지만, 반복자가 가리키는 대상 자체는 변경이 가능하다. 만약 변경이 불가능한 객체를 가리키는 반복자(즉, const T* 포인터의 STL 대응물)가 필요하다면 const_iterator를 쓰면 된다.
    • 가장 강력한 const의 용도는 함수 선언에 쓸 경우이다. 함수 선언문에 있어서 const는 함수 반환 값, 각각의 매개변수, 멤버 함수 앞에 붙을 수 있고, 함수 전체에 대해 const의 성질을 붙일 수 있다.
    • 함수 반환 값을 상수로 정해 주면, 안정성이나 효율을 포기하지 않고도 사용자측의 에러 돌발 상황을 줄이는 효과를 꽤 자주 볼 수
      있게 된다. 한 예로, 다음을 보자.


    class Rational { … };

    const Rational operator* (const Rational& las, const Rational& rhs);


    • operator*의 반환 값이 상수 객체일 이유를 어디서 찾겠느냐라고 생각할 것이다. 그런데 상수 객체로 되어 있지 않으면 사용자 쪽에서 저지르는 다음과 같은 어처구니없는 실수를 멍청히 지켜볼 수밖에 없다.



    Rational z, b, c;
    . . .
    (a * b) = c; // a*b의 결과에 operator= 를 호출하는 실수,
    if (a * b = c) // a * b == c를 호출하려고 했지만 오타로 인해 잘못 쓴 경우,



    • 위의 코드는 a 및 b의 타입이 기본제공 타입이었다면 용서 없이 문법 위반에 걸리는 코드이다. operator*의 반환 값을 const로 정해 놓으면 이런 경우를 미연에 막을 수 있다. 그렇기 때문에 상수 반환 값 지정이 정답이 되는 것이다.
  • 상수 멤버 함수

    • 멤버 함수에 붙는 const 키워드의 역할은 "해당 멤버 함수가 상수 객체에 대해 호출될 함수이다"라는 사실을 알려 주는 것이다. 이런 함수가 왜 중요할까? 이유는 2가지이다.
      첫째는 클래스의 인터페이스를 이해하기 좋게 하기 위해서인데, 그 클래스로 만들어진 객체를 변경할 수 있는 함수는 무엇이고, 또 변경할 수 없는 함수는 무엇인가를 사용자 쪽에서 알고 있어야 하는 것이다.
      둘째는 이 키워드를 통해 상수 객체를 사용할 수 있게 하자는 것인데, 코드의 효율을 위해 아주 중요한 부분이기도 하다. C++ 프로그램의 실행 성능을 높이는 핵심 기법 중 하나가 객체 전달을 '상수 객체에 대한 참조자(reference-to-const)'로 진해앟는 것이기 때문이다. 그런데 이 기법이 제대로 살아 움직이려면 상수 상태로 전달된 객체를 조작할 수 있는 const 멤버 함수, 즉 상수 멤버 함수가 준비되어 있어야 한다는 것이 바로 포인트이다.

    • 어떤 멤버 함수가 상수 멤버(const)라는 것이 대체 어떤 의미일까? 여기에는 굵직한 양대 개념이 자리잡고 있다. 하나는 비트수준 상수성[bitwise constness] (다른 말로 물리적 상수성(physical constness))이고, 또 하나는 논리적 상수성(logical constness) 이다.

      **비트수준 상수성은 어떤 멤버 함수가 그 객체의 어떤 데이터 멤버도 건드리지 않아야(정적멤버는 제외) 그 멤버 함수가 'const'임을 인정하는 개념이다. 즉, 그 객체를 구성하는 비트들 중 어떤 것도 바꾸면 안 된다는 것이다. 비트수준 상수성을 사용하면 상수성 위반을 발견하는 데 힘이 많이 들지 않는다. 컴파일러는 데이터 멤버에 대해 대입 연산이 수행되었는지만 보면 되기 때문이다. 사실 C++에서 정의하고 있는 상수성이 비트수준 상수성이다. 그리고 상수 멤버 함수는 그 함수가 호출된 객체의 어떤 비정적 멤버도 수정할 수 없게 되어 있다.

  • 이것만은 잊지 말자

    1) const를 붙여 선언하면 컴파일러가 사용상의 에러를 잡아내는 데 도움을 준다. const는 어떤 유효범위에 있는 객체에도 붙을 수 있으며, 함수 매개변수 및 반환 타입에도 붙을 수 있으며, 멤버 함수에도 붙을 수 있다.
    2) 컴파일러 쪽에서 보면 비트수준 상수성을 지켜야 하지만, 우리는 개념적인(논리적인) 상수성을 사용해서 프로그래밍해야 한다.
    3) 상수 멤버 및 비상수 멤버 함수가 기능적으로 서로 똑같게 구현되어 있을 경우에는 코드 중복을 피하는 것이 좋은데, 이때 비상수 버전이 상수 버전을 호출하도록 만들어야 한다.

  • 항목 4: 객체를 사용하기 전에 반드시 그 객체를 초기화하자

    • 객체를 구성하는 데이터의 초기화 순서는 어떤 컴파일러를 막론하고 항상 똑같다
      1) 기본 클래스는 파생 클래스보다 먼저 초기화된다
      2) 클래스 데이터 멤버는 그들이 선언된 순서대로 초기화된다

    • 정적 객체(static object) 는 자신이 생성된 시점부터 프로그램이 끝날 때까지 살아 있는 객체를 일컫는다. 그러므로 스택 객체 및 힙 기반 객체는 애초부터 정적 객체가 될 수 없다.
      정적 객체의 범주에 들어가는 것은 다음 조건을 만족한다.

      1) 전역 객체가 있다.
      2) 네임스페이스 유효범위에서 정의된 객체이다.
      3) 클래스 안에서 static으로 선언된 객체이다.
      4) 함수 안에서 static으로 선언된 객체이다.
      5) 파일 유효범위에서 static으로 정의된 객체이다.

      함수 안에 있는 정적 객체는 지역 정적 객체(local static object) 라고 하고, 나머지는 비지역 정적 객체(non-local static object) 라고 한다. 이 다섯 종류의 객체, 합쳐서 정적 객체는 프로그램이 끝날 때 자동으로 소멸된다. 다시 말해, main() 함수의 실행이 끝날 때 정적 객체의 소멸자가 호출된다는 이야기이다.

    • 번역 단위(translation unit) 는 컴파일을 통해 하나의 목적 파일(object file)을 만드는 바탕이 되는 소스 코드를 일컫는다. 여기서 번역은 소스의 언어를 기계어로 옮긴다는 의미이다. 기본적으로는 소스 파일 하나가 되는데, 그 파일이 #include하는 파일(들)까지 합쳐서 하나의 번역 단위가 된다.

    • 이것만은 잊지 말자

      1) 기본제공 타입의 객체는 직접 손으로 초기화한다. 경우에 따라 저절로 되기도 하고 안 되기도 하기 때문이다.
      2) 생성자에서는 데이터 멤버에 대한 대입문을 생성자 본문 내부에 넣는 방법으로 멤버를 초기화하지 말고 멤버 초기화 리스트를 즐겨 사용하자. 그리고 초기화 리스트에 데이터 멤버를 나열할 때는 클래스에 각 데이터 멤버가 선언된 순서와 똑같이 나열하자.
      3) 여러 번역 단위에 있는 비지역 정적 객체들의 초기화 순서 문제는 피해서 설계해야 한다. 비지역 정적 객체를 지역 정적 객체로 바꾸면 된다.

'Programming > C & C++' 카테고리의 다른 글

EC++Study_4  (0) 2019.12.15
EC++ Study_3  (0) 2019.09.01
EC++ Study_1  (0) 2019.08.07
C# Windows Forms Application GUI 기본  (0) 2018.07.27
Visual C++ Windows forms [1]  (0) 2018.07.06