오픈소스 라이센스 검색사이트

라이센스를 검색해보다가 발견한 사이트.

아마 프로그래머들이라면 특정 오픈소스 라이브러리를 가져다가 쓸 일이 많을텐데 이놈의 오픈소스들은 규격화된 라이센스조차도 수십가지인지라 도무지 이게 뭔내용인지 기억할래야 할수가 없다.

Boost 라이브러리를 쓰다가 Boost 라이센스라는게 있길래 이 라이센스는 도대체 내용이 뭐인지 찾다가 발견했다.

부스트 라이센스는 다음과 같았다,

https://olis.or.kr/license/Detailselect.do?lId=1070

각종 오픈소스 라이브러리를 사용할 때마다 한번씩 참고해봐야겠다.

auto_ptr, shared_ptr, scoped_ptr

스마트포인터

메모리를 사용하는 과정에서 할당이후 해제가 되지 않으면 해당 메모리 공간에는 다른 데이터를 넣을 수 없게 되어 사용가능한 메모리공간이 점점 줄어드는 메모리누수(leak)현상이 발생한다. 또한 해당 메모리 공간에 접근하거나 하여 쓰레기값이 나오거나 혹은 프로그램이 오류를 일으킬 수도 있다. 따라서 할당(malloc, new 등) 이후에는 반드시 해제(free, delete 등)을 해줘야하는데 이 과정의 복잡함과 프로그래밍에서의 실수를 방지하기 위해 자동으로 메모리를 해제해주는 포인터가 바로 스마트포인터이다.

1. auto_ptr

C++ STL에 있는 기본 스마트포인터. 객체가 특정 스코프(…{….}…)가 종료 될 때 소멸자가 호출된다는 원리를 이용한다.

기본적인 사용법

void main()
{
        auto_ptr<int> pInt(new int);
        *pInt = 10;
        cout<<*pInt<<endl;
}

auto_ptr의 단점

  1. 스코프 종료 시점에 메모리를 해제하기 때문에 메모리 할당을 배열 단위로 받을 때에는 제대로 메모리를 해제하지 못할 수 있다.
  2. auto_ptr로 할당한 포인터 변수를 서로 대입할 시 (ex : a = b) a와 b가 같은 곳을 가리키는 것이 아니라 a는 b가 가리키고 있던 주소가 들어가고 b는 NULL 상태가 된다. 이렇게 하는 이유는 a와 b가 같은 메모리를 가리키고 있다면 auto_ptr에 의하여 같은 메모리를 두번 해제하려 할 수 있기 때문이다. 따라서, auto_ptr은 같은 메모리 영역을 가리키는 auto_ptr을 2개 이상 생성할 수 없다.
  3. 위와 같은 문제점 때문에 STL의 컨테이너들에는 사용할 수 없다.

그래서 auto_ptr은 C++ 11 에서 deprecated 되었고 앞으로를 위해서라도 이것을 사용하면 안된다.

2. shared_ptr

auto_ptr의 문제점 때문에 boost에서는 shared_ptr이 생겨나게 되었다. 메모리 포인터가 가리키고 있는 객체의 수를 ‘레퍼런스 카운터(참조횟수)’라는 것을 이용하여 0에서부터 1씩 증가시키고,  메모리 해제가 될때 1씩 감소시키다가 0이 되면 메모리를 해제한다. 따라서 같은 메모리 영역을 가리키지 못한다는 단점을 극복하게 된다.

기본적인 사용법

#include <iostream>
#include <vector>
#include <boost/shared_ptr.hpp>

using namespace std;
using namespace boost;

class ClassA
{
public:
	ClassA(){cout << "ClassA()" << endl;}
	~ClassA(){cout << "~ClassA()" << endl;}
	void hello(){cout << "# hello()" << endl;}
};

vector< shared_ptr<ClassA> > vec;

int main () {
	shared_ptr<ClassA> obj(new ClassA());
	cout << "* use_count (new): " << obj.use_count() << endl;

	vec.push_back(obj);
	cout << "* use_count (push): " << obj.use_count() << endl;

	obj->hello();

	vec.pop_back();
	cout << "* use_count (pop): " << obj.use_count() << endl;

	obj.reset(new ClassA());
	cout << "* use_count (reset): " << obj.use_count() << endl;

  return 0;
}

shared_ptr로 할당된 객체는 use_count()라는 함수를 통해 레퍼런스 카운터의 상태를 알아낼 수 있다.

 shared_ptr의 단점

  1. 레퍼런스 카운터를 이용하기 때문에 환형 링크드리스트처럼 head->next->…->next->tail->head 식으로 계속 다음을 가리키는 경우에는 레퍼런스 카운터가 0이 되지 않아 메모리가 해제되지 않을 수 있다.

3. scoped_ptr

동적으로 할당되는 객체에 대한 포인터를 가진다. 자신이 삭제되면 가리키고 있는 객체도 자동으로 삭제시킨다. 이 포인터는 복사가 불가능하게 되어있으므로 소유권이나 참조카운트 등의 문제가 발생하지 않는다.

기본적인 사용법

// ScopedPtrTest.h
#include <iostream>
#include <memory>
#include <boost/scoped_ptr.hpp>

using std::cout;
using std::endl;

class Sample
{
   public:
       Sample() { cout << "생성" << endl; }
       ~Sample() { cout << "소멸" << endl; }
       void print() { cout << "print" << endl; }
};

// ScopedPtrTest.cpp
#include "ScopedPtrTest.h"

typedef boost::scoped_ptr<Sample> scopePtr;

void printStr(scopePtr* Ptr); // 값 복사가 불가능하므로 포인터 형태로 넘겨야 한다.

int main(void)
{
    scopePtr p1(new Sample());
    scopePtr p2(new Sample());

    p1->print();
    printStr(&p1); // 스마트 포인터 scoped_ptr(scopePtr)의 포인터를 넘긴다.

   /*
       // scoped_ptr - 자신이 가지는 포인터를 다른 인스턴스로 할당하거나 넘겨줄 수 없다.
       //                    한번 가리키는 객체에 대한 삭제의 책임을 전적으로 진다.
       //                    (복사가 빈번한 STL에서 사용할 수 없다.)

    p2 = p1             // 에러! 할당 불가!
    scopePtr p3(p1) // 에러!
   */

   return 0;
}

void printStr(scopePtr* Ptr) // 값 복사가 불가능하므로 call by reference(address)로 인자를 넘긴다.
{
    (*Ptr)->print();
}

scoped_ptr의 단점

  1. 포인터를 다른 변수에 복사하거나 할당할 수 없다.
  2. STL의 컨테이너에 넣을 수 없다.
  3. 동적으로 생성한 객체들의 배열을 만들고 싶을 때에는 scoped_array를 이용한다.

출처&참고URL

  • http://psychoria.blog.me/40155382971
  • http://sweeper.egloos.com/2826435
  • http://bangkert89.blog.me/90161420244
  • http://blog.naver.com/ddongtong18/120161513218
  • http://breaklee.blog.me/60137887986
  • http://sanaigon.tistory.com/72
  • http://codecooking.tistory.com/21
  • http://blog.daum.net/creazier/15309389
  • http://silped.tistory.com/3
  • http://blog.naver.com/jjmyann123?Redirect=Log&logNo=120168606246
  • http://blog.naver.com/xelona?Redirect=Log&logNo=70041650058

BlackBerry Playbook은 왜 한국개발자들에게 외면 받았는가

작년초에 열린 Adobe Refresh 2011 에서는 BlackBerry사의 Playbook이 소개되었다.

한국 시장에는 아직 팔고 있지 않다. 아마 여러분이 한국에서 이 제품을 제일 처음 보게 될 것이다. 라는 소개로 시작된 플레이북 소개는 꽤 흥미로웠다. 커널 수준에서의 Adobe AIR의 지원, RIM과 Adobe의 협력, 커널 수준에서 AIR의 지원으로 인해 상당한 속도를 구현할 수 있다는 점 등.

AIR는 느려터졌다는 느낌을 지울 수 없던 나도 플레이북 시연을 보고는 정말 놀랐다. AIR가 저렇게 부드럽게 돌아가다니. 아마 나뿐만이 아니라 그 자리에 있던 다른 플래시 개발자들도 마찬가지였을 것이다.

그날 이후 일주일 정도가 지난 후 난 Playbook Developer 사이트에 접속해보았다. 이미 깨끗하게 준비가 되어있었다. (안타깝게도 한국개발자들을 위한 준비는 전혀 없었던 것 같다.) 이 웹사이트에서는 플레이북 SDK와 시뮬레이터 등에 대한 안내를 하고 있었고 다운로드 받을 수 있게 친절하게 준비되어있었다. 허나 안타깝게도 한국어로는 전혀 준비되어있지 않았다. RIM의 한국지사가 있는지도 모르겠지만 한국지사가 있었다면 개발자들을 조금이라도 도와주려는 노력이 있었다면 어땠을까?

플레이북의 시뮬레이터는 VMWare를 이용하여 사용할 수 있는 ISO 형식의 가상이미지 파일이었는데 맥에서 자기네 기기 테스트를 위하여 가상머신을 이용한 시뮬레이터를 제공하는 것은 상당히 괜찮은 생각 같았다. VMWare라는 시스템에 대해서 난 거부감이 거의 없었고 사용방법도 매우 편리했다. 적어도 아이폰의 SDK와 시뮬레이터에 비해서는 말이다.

어찌됐던 그렇게 Playbook에서 돌아갈만한 간단한 프로그램을 만들었고 블랙베리에 앱을 등록하러 갔다. 그런데 등록과정이 너무 복잡했다.

한국어 페이지까지는 기대하지 않았지만 인증서를 발급받고 등록하고… 프로그램을 업로드하고… 하는 과정을 수십번 반복하면서 난 지쳐갔다. 프로그램 하나 만드는게 왜 이렇게 어렵던지…

결국에는 수십번의 시도와 수십일간의 시간낭비 끝에 그만두었다. 차라리 아이폰SDK 같은 경우는 도움말이라도 많이 찾을 수 있어서 다행인데 플레이북쪽에는 아무도 아는 사람도 없고 인터넷에서 정보를 찾기도 어려웠다. 물론 플레이북의 고객지원도 그다지 좋지 못했다.

QNX 운영체제(일부 사람들은 플레이북인 안드로이드를 사용하는줄 아나보다.)의 장점을 제대로 살리지 못한 것 같기도하고… 아무래도 우리나라에는 조금 생소한 QNX라는 운영체제에 대해서도 더 많이 홍보했어야하지 않나 싶다.

플레이북 개발을 포기하게 만들었던 또 하나의 이유는 RIM에서 우리나라에 블랙베리 정식유통을 포기했다. 따라서 내가 어플리케이션을 만든다하더래도 어차피 외국인만을 대상으로 해야할 것이고 내가 만들 수 있는, 만들어야 할 어플리케이션은 결국 계속 줄어든다. 그럴바에는 차라리 사람들이 많이 쓰는 아이폰쪽으로 가는게 낫겠지?

개발자들도 기기를 구할 수 없는… 그리고 언제 우리나라에 정식발매될지도 모를 이런 상황들이 한국개발자들에게 외면 받은 것 같다.

지금 네이버를 살펴보니 플레이북 16기가가 이 대략 15만원~25만원 선에 팔리고 있다. 25만원 정도면 미개봉도 구할 수 있다. 이제 플레이북은 결국 ‘싸구려 잉여타블렛’ 정도의 이미지 밖에 되지 않을 것이고 아이패드나 안드로이드 타블렛들과는 경쟁도 안될 것 같다. 따라서 개발자들도 더이상 이 플랫폼에는 관심을 가질 이유가 없어졌다.

우리나라가 아이패드에 대부분 점령 당했지만 그래도 꾸준히 진출해봤으면 어떨까 싶었다. 그래도 내가 보기엔 플레이북도 나쁘지 않았고 가격적인 메리트도 있었으니.

blackberry developer

지금 BlackBerry Developer 사이트를 가보니 내가 이 사이트를 처음 만났던 1년전에 비해 많이 바뀌었네. C++, 웹워크, AIR, 자바 모든 환경을 다 지원한다. 과연 이런 플랫폼 위에서 개발하던 사람들이 얼마나 블랙베리쪽으로 올지는 모르겠지만…

여튼 플레이북이 우리나라에서 잘 되길 바라던 마음이 있었는데 이제 망해버려서 아쉬운 마음도 있다. RIM은 그냥 우리나라에서 철수해도 될듯…

IOCP에 대한 좋은 설명 문서

IOCP에 대해 검색하다가 발견하게 된 좋은 문서. 데브피아의 이기탁님이 쓰신 문서라고 한다. IOCP에 대하여 자세하게 잘 설명하고 있는듯하다. 나중에 다시 생각날 때마다 보기 위해 여기에 글을 쓰고 해당문서를 저장해놓는다.

이렇게 퍼와도 되는지 모르겠다. 내가 가져온 곳은 http://sweeper.egloos.com/2811340 (당연히 저작권은 이기탁님에게 있다.)

힙과 스택

내가 면접 때 많이 들어봤던 질문 중의 하나였다.

“힙과 스택에 대하여 설명해보세요.”

C/C++을 공부하면서 당연히 배우는 과정 중의 하나인데 문제는 이런 내용은 C 처음 배울 때 나온 내용이라 기억의 저편으로 멀리 가버렸다는 점. 기억을 되살리기 위해 다시 정리해본다.

스택의 특징은…

  • 프로그램이 자동적으로 할당한 메모리 영역이다.
  • 자동변수와 파라미터들이 생성되고 스코프가 끝나면 사라지는 메모리 영역.
  • 함수가 실행된 뒤 원래 실행위치로 돌아오는 리턴값 역시도 스택에 저장된다.
  • 함수 안에서 선언된 변수는 모두 스택으로 들어간다.
  • 함수를 호출했을 때 현재 상태에 대한 값도 스택으로 들어간다.
  • LIFO 구조를 가진다. 먼저 들어간 자료가 가장 나중에 나오게 되며, 가장 나중에 들어간 자료가 가장 처음에 나오게 된다.
  • 윈도우 프로그램의 경우 기본 스택메모리의 크기는 1MByte이다.
  • 스택의 크기를 넘어가게 되면 Page Fault 인터럽트(즉, 스택오버플로우)가 발생하게 된다.
  • 모든 포인터 역시도 스택에 올라간다. 포인터 역시도 하나의 변수라는 것을 잊지말 것.
  • 사용할 스택의 크기는 컴파일 하는 순간 모두 정해진다. 따라서 실행 도중 메모리의 크기를 변경할 수 없다.
  • 스레드가 여러개라면 각 스레드마다 고유한 스택영역이 준비된다.
  • 하나의 메소드를 실행하기 위한 스택메모리의 묶음을 스택프레임이라고 부른다.
  • 어떤 변수를 선언했다면 변수명 자체는 스택에 변수 내용은 힙에 저장된다.

힙의 특징은…

  • 프로그래머가 스스로 할당한 메모리 영역이다.
  • 힙은 프로그램이 동적으로 할당받아 사용되는 메모리 영역. C에서의 malloc을 통해 생성하고 free를 통해 해제하며 C++에서는 new를 통해 생성하고 delete를 통해 메모리를 해제한다.
  • 힙 메모리가 부족하게 되면 메모리 이상현상으로 프로그램은 종료되게 된다.
  • 사용할 힙의 크기는 프로그램 실행 중 정해진다.
  • 동적 메모리할당은 프로그램 사용자들이 얼마나 데이터를 입력할지 예측할 수 없는 경우에 사용한다.
  • 여러 스레드가 있는 경우 힙 영역은 각 스레드가 공통으로 사용할 수도 있다.

이외에도 데이터영역과 텍스트영역(코드영역)이 있다.

데이터영역의 특징은…

  • 프로그램이 실행되고 난 후 종료될 때 까지 지워지지 않을 메모리 공간이다.
  • 따라서, 전역변수, Static 변수 등이 저장된다.
  • 프로그램당 한개의 영역이 할당된다.

텍스트영역(코드영역)의 특징은…

  • 프로그램의 실행코드를 저장한다. 프로그래머가 만든 코드, 함수는 모두 여기에 저장된다.
  • 역시 프로그램당 한개의 영역이 할당된다.

 

위의 내용들을 찾을 수 있었다. 다시금 찾아보니 생각보다 내용도 많고 차이점도 여러가지인 것 같다. 앞으로 더 찾아보며 여기에 추가해야겠다.

 

<참고자료>

  • http://blog.naver.com/brosvaby?Redirect=Log&logNo=165225008
  • http://panic.kldp.org/node/199
  • http://blog.naver.com/ysjin1212?Redirect=Log&logNo=110128934379
  • http://blog.naver.com/0bloodwind0?Redirect=Log&logNo=20127908176
  • http://blog.naver.com/zelet7?Redirect=Log&logNo=140015294688

랜덤확수의 확률 변경과 분포에 대한 문제

이 질문 역시 내가 면접에서 질문 받았던 문제이다. 틀렸던 문제를 다시 한번 살펴보고 공부하는 차원에서 오답노트에 적어둔다. (Q는 면접관님, A는 내가 답변한 것이다. 존칭은 생략한다.)

Q. 랜덤함수에서 0과 1이 나올 확률이 같은가?
A. 같다. 내가 알기로는 0~1까지의 소수가 나오는 것으로 알고 있다. (여기서 대답을 잘못했다. 플래시의 랜덤함수와 C에서의 랜덤함수를 헷갈렸던 것이다.)
Q. 정말? 그렇다면 0~10까지 나오게 하려면 어떻게 해야하는가?
A. r이 그 랜덤함수값이라고 한다면 (int)(r * 10)으로 구할 수 있다.
Q. 그렇게 한 경우에는 0~10까지 모든 수가 나올 확률은 같은가?
A. 같다고 알고 있다.
Q. 만약 1~5까지의 어떤 수를 뽑는 랜덤함수가 있다면 확률을 바꾸고 싶다. 1,5는 가장 적게 나오도록, 2,4는 그다음으로 많이 나오도록 3은 제일 많이 나오도록 결과적으로는 확률분포가 삼각형 모양이 되게 하고 싶다. 어떻게 하면 되겠는가?

여기까지가 질문이었는데 확률을 바꾸라는 말에 제대로 대답을 못했다.

일단 첫번째는 랜덤함수는 0~1까지의 소수가 나오는 것이 아니었다. 그리고 랜덤함수의 확률 분포는 생각보다 그렇게 고르지 않다고 한다. (이에 대한 자료는 http://agebreak.tistory.com/49 와 http://ljh131.tistory.com/131 에 있다.) 내 생각에는 랜덤을 반복할 수록 점점 확률이 고르게 분포하지 않을까 싶다.

위 면접관분이 질문했던 내용을 찾아보니 정확하게는 균등분포를 정규분포로 바꾸는 문제였다. 이에 대한 자료는 http://tadis.tistory.com/14 를 참조하면 될것 같다. 그런데 이 코드를 봐도 sqrt는 알겠지만 log 등을 사용하는 구간에서 잘 이해가 안된다. 더군다나 이 코드는 내가 확률을 조정하기보다는 정규분포에 맞추어 바꿔주는 역할을 하는 코드인 것 같다.

다음은 간단히 1부터 5까지 랜덤한 수를 100000개 뽑아보는 코드다.

#include <iostream>
#include <time.h>

using namespace std;
#define MAX_TEST 100000
int main()
 {
 int count[5] = {0};
 int percent[5] = {10, 20, 40, 20, 10};
srand((unsigned)time(NULL));
 for (int i = 0; i < MAX_TEST; i++)
 {
 int r = rand() % 5 + 1;
 count[r - 1]++;
 }
float total = 0.0;
 for (int i = 0; i < 5; i++)
 {
 cout << i + 1 << "의 갯수 :: " << count[i] << " - " << count[i] / (float)(MAX_TEST / 100) << "%" << endl;
 total += (count[i] / (float)(MAX_TEST / 100));
 }
return 0;
 }

여기서 알 수 있는건 분포가 정확하게 20%씩은 아니라는 점이었다.

이 코드를 1부터 5까지 랜덤한 수를 정해진 비율에 맞도록 100000개 뽑아내는 코드로 변경한 것이다.

#include <iostream>
#include <time.h>
using namespace std;
#define MAX_TEST 100000
int main()
 {
 int count[5] = {0};
 int percent[5] = {10, 20, 40, 20, 10};
srand((unsigned)time(NULL));
 for (int i = 0; i < MAX_TEST; i++)
 {
 int r = rand() % 100;
if (r < 10)
 {
 count[0]++;
 }
 else if (r < 30)
 {
 count[1]++;
 }
 else if (r < 70)
 {
 count[2]++;
 }
 else if (r < 90)
 {
 count[3]++;
 }
 else if (r < 100)
 {
 count[4]++;
 }
 }
 for (int i = 0; i < 5; i++)
 {
 cout << i + 1 << "의 갯수 :: " << count[i] << " - " << count[i] / (float)(MAX_TEST / 100) << "%" << endl;
 }
return 0;
 }

단순히는 이렇게 하면 정해진 비율에 유사하게 값이 나온다.

추가로 난수발생에 대한 좋은 글들은 http://oops.kldp.org/node/103420 , http://agebreak.tistory.com/49 , http://terzeron.net/wp/?p=1006 에서 찾을 수 있었다.

비율이 유사하게가 아니라 정확한 비율대로 나오려면 배열에 미리 가능한 수를 넣어놓고 랜덤하게 위치를 변경시키는 방법 밖에는 아직 생각나지 않는다. 조금 더 생각하고 공부해봐야 할 문제인 것 같다.

데드락 현상이란?

이 질문은 내가 여기저기 입사지원을 하고 면접을 보고 시험을 볼 때마다 매번 나왔던 질문과 문제들이다. 처음 한번은 이 문제에 대답을 못했고 그다음 다시 공부한 다음 다음번부터는 잘 대답했다.

신기한건 이 질문은 내가 면접을 본 몇군데 회사의 기술면접, 실기, 필기시험 때마다 매번 나온 문제였다. 프로그래머라면 반드시 알아야할 문제라고 생각해두자.

데드락 현상은 교착 상대라고도 하는데 예를 들어, A와 B라는 두개의 스레드가 있을 때 A 스레드는 B의 실행결과를 받아야만 종료할 수 있고 B 스레드는 A 스레드의 실행결과를 받아야만 종료할 수 있다면 두개의 스레드는 영원히 종료될 수 없고 죽지 않는 스레드로 영원히 남게 된다. 이런 현상을 데드락 현상이라고 한다.

한줄로 대답하자면 두 스레드가 서로의 실행결과를 무한히 기다리는 현상이라고 답할 수 있을 것 같다.

특히 모 게임회사에서 네트워크 게임을 만들 때 데드락 현상에 빠지지 않았느냐고 물어봤는데 아직까지는 그런적이 없다고 말했더니 면접관분이 갸우뚱하셨다. 이 문제에 대해 선생님께 질문 해본 결과 윈도우소켓이 알아서 처리하기 때문에 그런 현상이 안 생겼던 것이라고 답해주셨는데 아직 잘 이해가 가지 않는다. 검색해봐도 이런 내용에 대한 답은 찾을 수 없는데 계속 더 공부해봐야 할만한 내용인 것 같다.

오버로딩과 오버라이딩의 차이

이 질문도 내가 면접을 보며 나왔던 문제인데 막연히 알고 있던 내용이라 더 정확히 알고 나중에 다시 공부하기 위해 적어놓는다.

오버로딩과 오버라이딩의 차이에 대해 설명하여 쓰라는 질문이었다.

사실은 플래시와 자바를 쓸 때 인터페이스 구현 때문에 오버라이딩만 계속 써왔고 C++를 할 때는 연산자 오버로딩 외에는 오버로딩에 대해 아는게 없기도 하고 용어가 헷갈려서 정확하게 답변하지 못했었다. 두개를 비교설명하자면 복잡하지만 다음의 두 문장으로 정리할 수 있을 것 같다.

  • 오버로딩은 클래스 안에서 파라미터가 다른 여러개의 같은 이름을 가진 함수를 정의하는 것.
  • 오버라이딩은 어떤 클래스를 상속 받은 경우, 부모클래스의 함수를 다른 내용으로 재정의 하는 것.

이렇게 짧게 정리할 수 있을 것 같다.

참고한 자료는 http://blog.naver.com/jwlee0208?Redirect=Log&logNo=10136772809 , http://zzing8300.tistory.com/127 에서 볼 수 있다. 이 두 페이지만 봐도 도움이 많이 될 것 같다.

클래스와 구조체의 차이

이 질문은 내가 면접을 보러다니던 당시 받았던 질문들이며 내가 제대로 답하지 못했다고 생각하는 것이다. 이 문제에 대해 더 공부하고 잊지 않기 위해 여기에 써둔다.

Q. 클래스가 무엇인가?
A. 같은 성질을 같은 데이터와 함수들의 집합체이다.
Q. 그럼 구조체 역시도 데이터와 함수를 가질 수 있다. 클래스와 동일하게 구조체로도 만들 수 있는데 왜 클래스를 사용하는가?
A. 클래스는 상속이 가능하지만 구조체는 불가능하다.
Q. 구조체 안에 구조체를 넣을 수 있지 않는가?
A. 그렇다.

이 질문에 난 대답을 제대로 못했다. 생각해보니 구조체와 클래스는 비슷한 점이 많은 것 같은데 정확히 차이점을 찾아본 적이 없었다.

검색엔진 등을 통해 구조체와 클래스의 차이점을 찾아본 결과는 다음과 같다.

  1. 클래스와 구조체는 데이터타입을 생성한다는 점에서 유사하다.
  2. 구조체는 기본접근자가 public인데 클래스의 기본접근자는 private이다.
  3. 구조체는 데이터의 초기화가 불가능하지만 클래스는 데이터의 초기화가 가능하다.
  4. 구조체는 데이터의 value가 복사되지만 클래스는 데이터의 reference가 복사된다. 따라서 클래스는 얕은 복사가 일어나고 구조체는 깊은 복사가 일어난다.
  5. 구조체는 상속해줄 수도 상속 받을 수도 없다.
  6. 구조체를 그럼에도 사용하는 이유는 참조의 낭비를 막을 수 있으며 데이터에 직접 접근하기 때문에 속도가 더 빠르다.
  7. 구조체는 함수의 재정의가 안된다.

제일 중요한 점은 reference/value 복사의 차이가 아닌가 싶다. 이에 대한 설명은 http://rintiantta.blog.me/40114721626 에 가장 잘 나와있다. 이 코드를 보면 이해가 한번에 되는 것 같다.

클래스의 경우에는 어떤 클래스의 타입을 가지는 A객체를 만들고 B라는 객체가 A객체와 동일하게 복사한 다음, A객체의 값을 변경하면 B객체의 값도 변경된다. (reference 즉, 메모리주소가 복사되므로)

구조체의 경우에는 어떤 구조체의 타입을 가지는 A객체를 만들고 B라는 객체가 A객체와 동일하게 복사한 다음, A객체의 값을 변경하면 B객체의 값은 변경되지 않는다. (value, 즉, 값 자체가 복사되므로 B객체의 값은 변경되지 않는다.)

<참고자료>

http://blog.naver.com/fsclub2307?Redirect=Log&logNo=130113830445
http://rintiantta.blog.me/40114721626
http://cafe.naver.com/mbcasp/181

지하철에 개찰구는 왜 들어가는 곳보다 나오는 곳이 더 많을까?

이 질문은 내가 N모 게임회사의 기술면접 때 받았던 질문이다. 당시에는 제대로 대답하지 못했지만 내가 몰랐던 부분을 다시 생각해보고 기억해놓기 위해 오답노트에 써둔다.

Q는 면접관님이 나에게 했던 질문이고 A는 내가 대답한 것이다. (존칭은 생략)

Q. 올 때 지하철을 타고 왔나?
A. 그렇다.
Q. 지하철에 개찰구는 왜 들어가는 곳보다 나오는 곳이 많을까?
A. 원래는 수가 같은게 정상이고 현재는 출퇴근시간, 역특성에 따라 유동적으로 조정한다. 최근에는 하나의 개찰구로 나올 수도 들어갈 수도 있게 되어있다.
Q. 만약 그렇지 않고 일반적인 경우(그러니까 이건 예전 지하철 개찰구를 말한다. 나오는 곳과 들어가는 곳이 고정되어있는 경우)에는 들어가는 곳이 많을까 나오는 곳이 많을까?
A. 지금까지 관찰해본 결과, 나오는 곳이 많았다.
Q. 왜 그럴까?

여기까지였고 더이상 대답하기 어려웠다. 몇가지 힌트를 면접관분이 해주셔서 결국에는 답을 말했지만 어찌됐던 좋은 대답은 하지 못했던 것 같다.

집에 오며 관찰해본 결과 강남역의 경우에도 나오는 개찰구가 들어가는 개찰구보다 더 많았다. 다른 지하철역 몇군데도 그러했고 아마 다른 지하철역의 경우에도 마찬가지일 것이다.

집에 돌아오면서 곰곰히 생각해보고 경제학, 수학에 능한 누나와 대화해본 내가 내린 결론은 이렇다.

현재는 내가 대답한대로 최근에는 하나의 개찰구로 들어가는 것과 나오는 것이 가능하고 인구의 변화, 역특성에 따라 조정하는게 맞다. 그렇게 되어있지 않은 옛날 개찰구 방식이라면 나가는 개찰구가 더 많은데 그 이유는 지하철의 특성 때문이다.

지하철의 경우에는 승객이 타러 들어갈 때에는 입구가 하나이고 승객이 한명씩 들어가도 플랫폼에서 기다리게 되므로 상관이 없지만, 지하철이 역에 도착하여 승객이 내리는 경우에는 한번에 많이 내리게 된다. 그렇기 때문에 나오는 개찰구의 수가 더 적다면 승객은 나오지 못하고 개찰구 앞에서 밀리게 된다. 여기서 중요한 것은 승객이 들어가는 경우에는 한명씩 들어가도 상관이 없지만 내리는 경우에는 ‘한번에 많이’ 내리게 된다는 점이다.

더 생각해보니 이걸 프로그래밍과 연관지어보면 입력과 출력이 동시에 일어날 때 출력은 기다리다가 폭발적으로 일어나고 입력은 천천히 지속적으로 일어나는 경우라고 생각된다. 이런 경우 지하철 플랫폼이 일종의 버퍼 역할을 해준다고 볼 수 있을 것 같다.

위 질문을 받았을 때 이렇게 생각했다면 좋았겠지만 그떈 너무 당황하고 긴장해서 제대로 답하질 못했다. 승객이 내릴 때와 승객이 승차하러 들어갈 때의 차이점마저 설명하지 못했고, 그러니 프로그래밍과 연관지어 생각해볼 수도 없었다.

상당히 재미있는 질문이었다. 일상생활에서 일어나는 일을 별 생각해보지 않고 지냈는데 이 일을 계기로 모든 일을 논리적으로 생각해봐야겠다는 생각이 든다. N사의 면접관님께 감사드리며.