C++은 그 기본부터 C에서 온 친구로서 사실상 C에서 사용할 수 있는 것들은 대부분 C++에서도 사용 할 수 있다. 오히려 C에서 불편하던 여러가지를 개선 시킨 것이 C++이라 할 수 있는데 이 때문에 C에서 불편하던 것들이 무엇인지 안다면 C++로 넘어가면서 어떤 방식으로 개선되었는지를 직관적으로 이해하기 쉽다. 

 

이번 포스팅에서는 C에서 C++로 넘어가는 와중에 필요한 지식을 간단히 설명하려 한다. 

 

여러분들이 C에 대해 대부분의 내용을 이해했다고 가정하고 설명하도록 할 것이기 때문에 C에 대한 내용은 언급만 하고 넘어갈 예정이다. 

 

1. namespace 

 

namespace란? 이름 공간. 함수, 변수, 클래스등의 하나로 묶어서 접근을 특정 이름만으로 할 수 있도록 제한하는 키워드 

 

 C에서 사용하는 여러 라이브러리의 속을 한번 뜯어봤다면 봤을 만한 함수나 변수명이 있을 것이다. __로 시작하는 이름들이 그것인데 이런 방식으로 사용하는 근본적인 이유는 함수명이나 변수명의 중복을 피하기 위해서다. (일종의 약속이라고 보면 된다.)

 

 이런 것들은 C의 태생적 한계 때문이기도 한데 함수의 경우 그 이름을 전역으로 밖에 선언하지 못하고 따라서 여러 라이브러리를 사용할 경우 이름이 겹치는 위험이 발생할 수 있기 때문이다. 이를 방지하기 위해 C에서는 static 키워드를 제공하는데 (특정 파일 안에서만 접근 가능 ) 이것으로는 부족한 것이 현실이다. 

 

 이를 해결하기 위한 좋은 방법으로 C++에서 제공하는 namespace기능을 이용할 수 있다. 이 기능을 사용하면 변수명이나 함수명이 겹치더라도 잘 처리할 수 있다. 

 

 이 namespace라는 것을 사용하는 방법은 아래와 같다. 

 

 

 

 일반적으로 C++로 프로그램을 짤 때 많이 사용하는 STL(Standard Template Library)은 모두 std라는 namespace에 묶여있으며 이 안에는 최대 값을 구해주는 함수가 있다. 이 함수는 STL library중에서도 algorithm 헤더파일에 들어 있는데 이를 사용하는 모습은 아래와 같다. 

 

1
2
3
4
5
6
#include <stdio.h>
#include <algorithm>
int main(void){
    int max = std::max(10,20);
    printf("%d",max);
}
cs

 

 

2. struct, class

 

 struct, class란? 여러 자료형을 한곳에 묶어 관리하기 위한 하나의 '구조'로 필요에 따라 접근을 제한 하고, 해당하는 자료를 처리하기 위한 루틴을 거치도록 만들 수 있다. 

 

 일반적으로 struct는 data에 관한 자료를 다루기 위해 사용하며, class는 그 외의 다양한 자료를 다루기 위해 사용한다. (ADT 같은 것들)

 

 C에서도 struct라는 키워드가 있다. 이는 C언어에서 제공하는 아주 강력한 기능으로 그 쓰임세는 정말 다양하여 모두 나열할 수는 없을 것이다. 하지만 그 대부분의 쓰임세는 struct의 특징인 여러 자료형을 한 곳에 묶어 (의미적으로, 공간적으로) 관리하기 편하게 만들었다는 점에서 기인한다. 하지만 C의 이런 방식은 한계가 존재하는데 바로  누구나 해당 자료형의 데이터에 접근하고 수정할 권한이 있다는 것이다. 

 

 ADT의 예를 들어보자. ADT에는 추상화라는 개념이 있다. '추상화'란 사용자가 그 내부의 복잡한 구현을 알 필요없이 그 기능들을 가져다 쓰게 함으로 써 실질적으로 필요로 하는 것을 구현하고 설계하는데 집중 하게하는 것이다. C에서 이를 쉽게 구현하는 법은 구조체에 담아 관리하고 이 구조체를 다루는 함수를 제공하는 방식인데 이 방식에는 약간의 문제가 있다.

 그 문제는 접근을 제한할 수 없기 때문에 발생하는데 대표적으로 struct의 field에 사용자가 접근하여 값을 바꾸는 것이다. 

 

ADT의 가장 대표적인 예인 double linked list를 예로 들어보자.  

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
 
struct linked_node {
    int data;
    struct linked_node * prev, * next;
};
struct linked_list {
    int size;
    struct linked_node * front* back;
};
bool linked_empty(struct linked_list * list){
    return list->size == 0;
}
int main(void){
    struct linked_list list;
    linked_init(&list);
    list.size = 1000;
    if(linked_empty(&list)){
        printf("empty");
    }else printf("not empty");
}
cs

 

 

위 코드의 linked_empty()라는 함수는 해당 리스트가 비어있다면 true를 비어있지 않다면 false를 리턴하는 C 코드이다.  만약 이 때 어떤 사용자가 main 처럼 마음대로 접근을 해버렸다고 해 버리자. 

 

 그렇다면 이 ADT는 원래의 역할을 제대로 다하지 못할 것이다. 

 

이 처럼 특정 데이터에 접근하는 것을 제한할 필요성이 있는데 이를 C++에서는 접근제어자로 해결할 수 있다. 접근 제어자는 크게 3가지 종류가 있는데 아래와 같다. 

  • private
    • 외부에서 접근이 불가능하고 내부에서만 접근이 가능하다. 
  • public
    • 외부에서 접근이 가능하다.
  • protected
    • 상속과 관련된 내용. 추후에 다룸

 위 키워드들은 아래와 같이 사용할 수 있다. 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
 
 
struct linked_list {
private :
    int size;
 
    struct linked_node {
        int data;
        struct linked_node * prev, * next;
    }  * front* back;
public :
    linked_list() {
        size = 0;
        front = back = (linked_node *)0;
    }
    bool empty(){
        return size == 0;
    }
};
int main(void){
    linked_list list;
 
    list.size = 1000//에러
 
    if(list.empty()){
        printf("empty");
    }else printf("not empty");
}
cs

 13~16은 생성자라는 친구로 C에서는 없는 개념이다. linked_init에 해당하는 친구다. 

 

 24번째 줄에서 에러가 나는 것을 확인 할 수 있는데 private로 접근이 제한되었기 때문이다. 이렇게 잘못된 코드를 런타임 에러나 잘못된 동작이 아니라 컴파일 에러로 찾아 낼 수 있다는 것은 더 안전한 프로그램을 개발 할 수 있게 만들어준다. 

 

 상속이나 생성자, 소멸자에 관한 내용은 다음번에 다시 다루기로 하겠다. (지금은 필요 없음 ㅎ)

 

 c++에서 struct와 class는 큰 차이가 없는데 이는 c++이 객체지향적이기도하고 절차지향적이기도 한 과도기적 언어이기 때문이다. 다른 언어의 경우 이 두 키워드를 차이를 둬서 다루게 된다. 

 

 c++에서 두 키워드의 유일한 차이

  •  struct field의 default 접근제어자가 public
  •  class field의 default 접근제어자가 private

더 자세한 내용은 다음에 다루도록 하겠다. 

 

3. template

 

template란? template이란 단어 그 자체의 이름 그대로 형틀이다. 특정한 형틀을 가지고 자료형,함수등 새로운 것들을 찍어내는 것이라고 보면 된다. input으로 자료형을 넣으면 새로운 자료형, 함수, 클래스 등을 찍어 낼 수 있다.

template은 기존의 C언어에서 대응되는 개념이 거의 없는데 비슷한 것을 꼽아보라면 void *형이라고 보면 될 듯 싶다. 이는 void *보다 더 많은 정보를 담고 있는 친구이다. 

 

여러분은 C언어의 stdlib.h에 존재하는 qsort라는 함수를 사용한 경험이 있을 것이다. 이 함수는 quick sort를 구현해 놓은 친구로 어떤 배열형이든 받아 정렬해주는 함수이다. 이 함수의 원형은 다음과 같다. 

 

1
2
void qsort (void *base, size_t num, size_t size,
             int (*compare)(const void *const void *))
cs

혹시 모르는 사람들을 위해서 설명하자면 어떤 배열(base)과 그 배열이 담고있는 원소의 갯수(num), 그리고 원소 하나의 바이트수(size)와 각각의 배열을 어떻게 비교할지 결정하는 비교함수(compare)를 인자로 넘기면 그 배열을 quick sort로 정렬해주는 함수다. 

 

 C언어에서는 이런 임의의 배열을 표현할 방법이 없기 때문에 void *형이라는 꼼수를 사용해서 이를 구현해야 했다. 반면 C++에서는 이런 임의의 무언가를 표현할 좀더 직관적이고 안전한 방식을 제공하는데 그것이 바로 template이다. 

 사실 이런 template는 class부터 기본 타입, 때로는 상수도 들어갈 수 있으며 여러가지 장난질을 칠 수 있다. 이런 template으로 함수나 클래스등을 찍어 낼 수 있으며 이러한 자세한 내용에 관해서는 나중에 다루도록 하겠다.

 

 

template의 사용법은 대체로 "template이름<자료형>" 형태로 사용하게 된다. 위의 경우에는 template이름은 my_sort고 자료형들은 int나 double이 들어간 것이다. 때로는 이 <> 를 사용하지 않고도 사용할 수 있는데 이는 컴파일러가 자동으로 타입을 추론할 수 있는 경우에 한한다.   

 

template의 자세한 구현방법에 대해서는 다음에 더 자세한 내용을 다룰 것이다.

 

 

4. STL container

 

 STL이란? Standard Template Library의 약자로 C++에서 제공되는 표준화된 라이브러리다. 이 라이브러리는 쓰레드, 알고리즘, 특정한 ADT등 들을 다루기 위한 여러가지 모듈들을 이미 구현해놓았다.

 Container란? ADT들을 실질적으로 구현해놓은 것들이다.

 아마 다루고 넘어갈 것들로는 아래와 같다.

  • vector
  • deque
  • list
  • stack
  • queue
  • priority_queue
  • bitset
  • map
  • set
  • unordered_map
  • unordered_set
  • multiset
  • multimap
  • unordered_multiset
  • unordered_multimap

 

 

+ Recent posts