{{ label!='' ? 'Label : ' : (q!='' ? '검색 : ' : '전체 게시글') }} {{ label }} {{ q }} {{ ('('+(pubs|date:'yyyy-MM')+')') }}

[C++] The C++ Programming Language: 2. A Tour of C++: The Basics (1)

2. A Tour of C++: The Basics


2.1 Introduction

  이 챕터와 다음 세 챕터의 목표는, 잠시 세부사항은 접어두고 C++가 진정 무엇인지 여러분들에게 소개하는 것입니다. C++의 메모리와 컴퓨팅, 프로그램에 코드를 구성하는 기초 메커니즘 등에 대한 모델 및 C++의 표기법 등을 이야기할 것입니다. C++는 절차지향 프로그래밍이라고도 불리는 C에서 볼 수 있는 많은 스타일들을 제공합니다. 챕터 3은 C++의 추상화 메커니즘을, 챕터 4와 챕터 5는 표준 라이브러리 기능을 소개할 것입니다.

  전제는 여러분들이 프로그래밍을 이미 해봤어야 한다는 것입니다. 만약 그렇지 않다면, 다른 책인 Programming: Principles and Practice Using C++ [Stroustrup, 2009]을 먼저 읽어보시길 권합니다. 여러분들이 이전에 프로그래밍을 해봤더라도, 여러분들이 사용했던 언어는 아마 C++가 제공하는 것과 꽤 다를 것입니다. 앞으로 펼쳐질 "초고속 투어"가 조금 혼란스럽다면, 좀 더 자세한 내용을 다루는 챕터6으로 건너 뛰도록 하세요.

  이 C++ 투어는 초기에도 여러분들이 풍부한 기능들을 사용할 수 있도록 만들어줌으로써, 엄격한 상향식(bottom-up) 언어 설명으로부터 구제해줍니다. 예를 들어, 반복문은 챕터 10까지 이야기하지 않습니다. 비슷하게 클래스, 템플릿, 자유 공간(free-store, 힙) 사용, 표준 라이브러리 등에 대한 서술은 많은 챕터들에 퍼져 있습니다. 하지만 표준 라이브러리 자료형인 vector, string, complex, map, unique_ptr, ostream 등은 코드 예제를 강화하기 위해 어디든지 필요하면 쓰일 것입니다.

  비유적으로, 뉴욕이나 코펜하겐과 같은 도시로 관광을 떠난다고 생각해보죠. 몇 시간 동안, 여러분들은 관광 명소들을 둘러보고, 그 역사들을 듣고, 다음에 무얼 볼지에 대한 이야기들을 듣죠. 허나 여러분들은 여전히 그 도시를 완전히 알 수 없습니다. 보고 들은 것도 완전히 이해할 리 없죠. 진짜 도시를 이해하는 방법은, 그곳에 몇 년 동안 거주해보는 것입니다. 하지만 운이 좋다면, 약간의 개요와 도시의 특별한 점에 대한 개념 및 관심을 가질만한 아이디어들을 얻을 수 있습니다. 이 투어가 끝나면, 진짜 탐험이 시작되는 것이죠.

  이 투어는 C++을 통합된 전체로서 설명합니다. 구역을 나누지 않죠. 따라서, 우리는 C, C++98, C++11 등의 언어 기능들에 대해 식별하지 않을 것입니다.


2.2 The Basics

  C++는 컴파일 언어입니다. 프로그램이 실행되려면, 소스 텍스트는 컴파일러에 의해 처리되고, 목적 파일을 생성하고, 링커에 의해 조합되어 실행 가능한 프로그램이 되죠. C++ 프로그램은 보통 수많은 소스 코드 파일들로 구성됩니다.



  실행 가능한 프로그램은 특정한 하드웨어/시스템의 조합에 대해 생성됩니다. 따라서 맥 환경의 프로그램을 윈도우 환경에 이식할 수 없다는 의미입니다. C++ 프로그램의 호환성에 대해서 이야기할 때는 보통 소스 코드의 호환성을 이야기하는 것입니다. 소스 코드는 다양한 시스템에서 성공적으로 컴파일되고 실행될 수 있습니다.

  ISO C++ 표준은 두 종류의 개체로 정의합니다.

  • Core language features: 빌트인 타입(char, int 등)과 반복문(for문과 while문)
  • Standard-library components: 컨테이너(vector, map 등)과 입출력 연산(<<와 getline() 등)

  표준 라이브러리 컴포넌트는 모든 C++ 구현에서 제공되는 완전히 일반적인 C++ 코드입니다. 즉, C++ 표준 라이브러리는 C++ 스스로 구현될 수 있습니다.(아주 가끔, 스레드 컨텍스트 스위칭 등을 위한 기계 코드가 사용되긴 합니다) 곧 C++는 가장 까다로운 시스템 프로그래밍 작업에서도 충분히 표현력 있고 효율적이라는 것을 의미합니다.

  C++는 정적 타입 언어입니다. 즉, 모든 개체(객체, 값, 이름, 수식 등)의 자료형은 반드시 이것의 사용 시점에 컴파일러가 알아야만 합니다. 객체의 자료형은 그것에 적합한 연산의 집합을 결정합니다.


2.2.1 Hello, World!

2.2.2 Types, Variables, and Arithmetic

(생략)

2.2.3 Constants

  C++은 불변성(immutability)에 대해 두 가지 표기법을 지원합니다.

  • const: 쉽게 말해 "이 값을 변경하지 않겠다고 약속"하는 것입니다. 이는 인터페이스를 명세하기 위해 주로 사용되며, 함수에서 전달된 데이터가 변경될 두려움 없이 사용할 수 있게 만들어줍니다. 
  • constexpr: 쉽게 말해 "컴파일 시기에 결정"되어야 하는 것입니다. 이는 상수를 명세할 때 주로 사용되며, 성능을 위해 손상될 가능성이 없는 메모리에 이를 배치할 수 있도록 해줍니다.

  다음을 참고하세요.

1
2
3
4
5
6
7
8
9
const int dmv = 17// dmv is a named constant
int var = 17// var is not a constant
constexpr double max1 = 1.4∗square(dmv); // OK if square(17) is a constant expression
constexpr double max2 = 1.4∗square(var); // error : var is not a constant expression
const double max3 = 1.4∗square(var); //OK, may be evaluated at run time
double sum(const vector&lt;double&gt;&amp;); // sum will not modify its argument (§2.2.5)
vector&lt;double&gt; v {1.23.44.5}; // v is not a constant
const double s1 = sum(v); // OK: evaluated at run time
constexpr double s2 = sum(v); // error : sum(v) not constant expression
cs


  함수에도 역시 적용 가능합니다. 이 때의 수식은 컴파일러에 의해 결정될 것입니다. 반드시 constexpr로 정의되어야 하죠. 다음을 참고하세요.

1
constexpr double square(double x) { return x∗x; }
cs


  constexpr을 사용하려면 간단한 함수여야 합니다: 값을 계산하는 return문일 뿐이기 때문이죠. constexpr 함수는 non-constant 인자와 함께 쓰일 수도 있습니다. 하지만 이 경우에는 반환 결과가 constant expression이 아닙니다. 


2.2.4 Tests and Loops

2.2.5 Pointers, Arrays, and Loops

(생략)


2.3 User-Defined Types

  C++의 다양한 자료형들은 직접적이고 효율적으로 일반적인 컴퓨터 하드웨어의 역량을 반영합니다. 하지만 고급 응용프로그램을 편리하게 작성하기 위한 높은 수준의 기능들은 프로그래머들에게 제공되지 않죠. 대신, 자료형을 추상화할 수 있는 수단을 제공합니다. 사용자 정의 자료형이죠.


2.3.1 Structures

2.3.2 Classes

2.3.3 Enumerations

(생략)


2.4 Modularity

  C++ 프로그램은 수많은 부분들로 이루어져 있습니다. 가령 함수, 사용자 정의 자료형, 클래스 계층, 템플릿 등이죠. 이러한 것들을 관리하는 열쇠는 이 사이의 상호작용을 명확하게 정의하는 것입니다. 인터페이스 부분과 구현 부분을 분리하는 것은 이러한 것의 첫걸음이자 가장 중요한 단계입니다. 언어 수준에서 C++는 인터페이스를 선언으로 표현할 수 있습니다. 선언(declaration)은 함수나 자료형을 사용하기 위해 필요한 모든 것들을 명세합니다. 다음을 참고하세요.

1
2
3
4
5
6
7
8
9
10
11
double sqrt(double); // the square root function takes a double and returns a double
 
class Vector {
public:
    Vector(int s);
    double& operator[](int i);
    int size();
private:
    double∗ elem; // elem points to an array of sz doubles
    int sz;
};
cs


  여기서 핵심은 바로 함수의 바디입니다. 함수의 정의가 "다른 곳"에 있다는 것이죠. 이 예제에서, Vector 역시 "다른 곳"에 정의되게 만들 수 있지만, 이는 다음에 다뤄보도록 하죠. sqrt()의 정의는 아마 다음과 같을 것입니다:

1
2
3
4
double sqrt(double d) // definition of sqrt()
{
    // ... algorithm as found in math textbook ...
}
cs

  

Vector의 경우, 역시 다음과 같은 멤버 함수들을 정의해야겠네요.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Vector::Vector(int s) // definition of the constructor
    :elem{new double[s]}, sz{s} // initialize members
{
}
 
double& Vector::operator[](int i) // definition of subscripting
{
    return elem[i];
}
 
int Vector::siz e() // definition of size()
{
    return sz;
}
cs


  우리는 반드시 Vector의 함수를 정의해야 하지만, sqrt()는 그렇지 않습니다. 표준 라이브러리의 일부이기 때문이죠. 그러나 이는 완전히 동일합니다: 어쨌거나 라이브러리(의 구현)도 역시 "다른 코드"에 적혀있을 것이기 때문이죠.


2.4.1 Separate Compilation

  C++는 유저 코드가 오로지 선언과 함수의 사용만을 볼 수 있도록 별도로 컴파일하는 개념을 지원합니다. 이러한 자료형과 함수의 정의는 분리된 소스 파일에 존재하게 되며, 각각 컴파일되죠. 곧 프로그램을 반독립(semi-independent)적인 코드 조각의 집합으로 구성할 수 있게 만들어줍니다. 이는 컴파일 시간을 최소화하고 논리적으로 구분되어야 하는 부분들을 엄격하게 구분해주죠.

  보통 우리는 모듈의 인터페이스를 명세를 그것의 사용 목적에 알맞는 이름으로 된 파일에 선언합니다. 

1
2
3
4
5
6
7
8
9
class Vector {
public:
    Vector(int s);
    double& operator[](int i);
    int size();
private:
    double∗ elem; // elem points to an array of sz doubles
    int sz;
};
cs


  이는 Vector.h라는 파일에 선언되며, 사용자들은 해당 파일을 include하여 헤더 파일(header file)을 호출하며, 인터페이스에 접근합니다. 다음을 참고하세요.

1
2
3
4
5
6
7
8
9
10
11
12
// user.cpp:
#include "Vector.h" // get Vector’s interface
#include <cmath> // get the the standard-librar y math function interface including sqrt()
using namespace std; // make std members visible (§2.4.2)
 
double sqrt_sum(Vector& v)
{
    double sum = 0;
    for (int i=0; i!=v.siz e(); ++i)
    sum+=sqrt(v[i]); // sum of square roots
    return sum;
}
cs


  컴파일러가 일관성을 보장하도록 만들기 위해, .cpp 파일은 Vector의 구현을 제공하며 이 역시 .h 파일을 include하여 해당 인터페이스를 제공합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Vector.cpp

Vector::Vector(int s) // definition of the constructor
    :elem{new double[s]}, sz{s} // initialize members
{
}
 
double& Vector::operator[](int i) // definition of subscripting
{
    return elem[i];
}
 
int Vector::siz e() // definition of size()
{
    return sz;
}
cs


  user.cpp와 Vector.cpp의 코드는 Vector.h에 표현된 Vector의 인터페이스를 공유합니다. 하지만 두 파일은 완전히 독립적이며 따로따로 컴파일되죠. 시각적으로는 다음처럼 표현할 수 있습니다.


  엄격히 따지자면, 분리된 컴파일을 사용하는 것은 언어의 문제는 아닙니다. 특정 언어 구현의 이점을 어떻게 최대한 활용할 수 있느냐의 문제죠. 그러나 이는 실용적으로 매우 중요합니다. 최고의 접근 방법은 모듈화를 최대화하고, 언어 기능들을 통해 모듈화를 논리적으로 표현하고, 파일들을 통해 물리적으로 모듈화를 이용해 분리된 컴파일을 실현하는 것이죠.


2.4.2 Namespaces

(생략)

댓글

이 블로그의 인기 게시물

[코딩의탑] 4층: 툰 쉐이딩

[코딩의탑] 3층: 바다 렌더링

[코딩의탑] 5층: 포탈(Portal), 더 나아가기