관리 메뉴

Kim's Programming

C++ - 클래스 상속(1/3) 본문

Programming/Cplusplus

C++ - 클래스 상속(1/3)

Programmer. 2015. 9. 22. 07:06

상속


클래스 확장


상속은 캡슐화, 추상화와 함께 객체 지향 프로그래밍의 중요한 특징중 하나입니다. 캡슐화와 추 상화는 객체가 온전한 부품이 될 수 있는 방법을 제공하는데 비해 상속은 클래스를 좀 더 쉽게 만들 수 있는 고수준의 재사용성을 확보하고 클래스간의 계층적인 관계를 구성함으로써 객체 지향의 또 다른 큰 특징인 다형성의 문법적 토대가 됩니다. 상속(Ingeritance의 사전적 의미는 자식이 부모가 가진 모든 것을 물려받는 것을 의미하는데 OOP의 상속도 기본적인 의미는 동일합니다. 이미 정의되어 있는 클래스의 모든 특성을 물려받아 새로운 클래스를 작성하는 기법을 상속이라고 합니다. 흔히 상속은 이미 만들어진 클래스를 재활용하기 위한 기법으로 소개되며 재활용이 상속의 가장 큰 장점이기는 하지만 상속에 의해 부차적으로 발생하는 효과도 있습니다. 상속을 하는 목적 또는 상송에 의한 효과는 다음 세 가지로 요약할 수 있습니다.

  1. 기존의 클래스를 재활용한다. 가장 기본적인 효과
  2. 공통되는 부분을 상위 클래스에 통합하여 본복을 제거하고 유지, 보수를 편리하게 한다.
  3. 공동의 조상을 가지는 계층을 만듦으로써 객체의 집합에 다형성을 부여한다.
상속의 이런 세 가지 목적을 모두 이해하고 100%활용할 수 있다면 상속을 모두 정복했다고 할 수 있습니다. 두 번쨰, 세 번쨰 효과는 조금 어려우므로 우선 상대적으로 쉬운 재활용에 대한 문제부터 고찰해보겠습니다. 클래스는 필요한 멤버를 모두 포함하고 적절히 멤버를 숨겨 자신을 방어함으로써 프로그램의 부품으로 사용됩니다. 그러나 한 번 만들어진 클래스가 언제까지고 어느 곳에서나 그대로 게쏙 사용될 수 있는 것은 아닙니다. 외부 세계의 요구가 끊임없이 변화하고 객체가 동작하는 환경이 각기 다르기 때문에 완성된 클래스에 기능을 추가하거나 변경해야 하는 경우는 아주 빈번합니다.

클래스를 처음 디자인할 때부터 범용성과 이식성을 확보하기 위해 굉장히 많은 노력을 합니다. 경험이 많은 개발자일수록 초기에 다양한 상황을 충분히 고려하여 클래스를 디자인할 것이며 이런 노력들이 확실히 효과가 있어서 잘 설계된 클래스는 훨씬 안정적이고 재사용될 수 있는 범위도 넓습니다. 그러나 아무리 경험이 많고 모든 것을 고려한다하더라도 미래의 일까지 예측하는 것은 불가능하기 때문에 재사용을 위해 클래스를 수정해야 하는 상황을 근본적으로 피할 수는 없습니다.

사람의 이름과 나이를 표현할 수 있는 Person 클래스에 약간의 멤버를 더 추가하면 좀더 기능이 복잡한 대상을 표현할 수 있습니다. Person 클래스에 월급, 근무 시간이라는 속성과 출근한다, 일한다 등의 동작을 추가하면 직원(staff)클래스가 될 것이고 계끕 보직 등의 속성과, 훈련, 전투등의 동작이 되면 군인(Soldier)클래스가 될 것입니다. 표현하고자 하는 대상이 복잡하고 구체적일수록 추가되어야 하는 속성과 동작의 개수는 많아질 것입니다.Person 클래스에 학번(StNum)이라는 속성과 공부한다(Study)는 동작을 추가해서 학생(Student)이라는 대상을 표현해야 한다고 해보겠습니다. 물론 완전한 학생이 되기 위해서는 이외에도 전공, 학년, 성적 등의 속성과 수업 듣는다 시험친다. 미팅하낟 등의 다양한 동작이 더 필요하지만 학번과 공부만 한다고 하겠습니다.

일단 기존 클래스를 원하는 대로 마음대로 뜯어 고치는 방법을 생각할 수 있습니다. person이라는 이름을 Student로 바꾸고 int StNum;이라는 멤버 변수와 Study라는 멤버 함수를 추가합니다. 물론 클래스의 이름이 바뀌었으므로 클래스와 같은 이름을 사용하는 생성자, 파괴자의 이름은 반드시 바꿔야합니다. OutPerson도 OutStudent로 바꾸는 것이 좋겠지만 일단 이름은 그대로 두고 학번을 출력하는 코드만 추가하겠습니다. 이렇게 되면 과연 Person이 Student가 되며 새로 만든 Student로 학생이라는 실제 대상을 잘 표현 할 수 있습니다. 그러나 Person을 Student로 바꿔버렸기 떄문에 기존에 존해라던 Person 클래스는 사라져버리며 이미 이 클래스를 사용중이 었다면 문제가 됩니다. Student를 만드는 데는 성공했지만 기존의 클래스가 파괴되어 버렸으므로 이것은 변경이지 재활용이라고 볼 수 없습니다. 원본을 유지한 채로 새로운 클래스를 만들려면 기존 클래스를 복사하여 사본을 만든후에 사본을 뜯어 고쳐야합니다. 이럴 떄 상속을 사용합니다.

상속을 할 때 원본 클래스가 어떤 것이라는 것을 밝히고 이 외에 더 필요한 멤버를 추가로 선언합니다. 그러면 컴파일러는 원본 클래스의 모든 멤버에 대한 선언문을 가져오고 추가로 선 언한 멤버도 클래스 안에 같이 포함시킵니다. 전통적인 방법에 비해 복사해서 붙여 넣고 기존 멤버에 대한 선언문을 가져오는 동작을 컴파일러가 대신한다는 점이 다릅니다. 개발자는 상속된 클래스에 원하는 추가 멤버만 더 선언하면 됩니다. 기존 클래스의 재활용만을 목적으로 한다면 사실 복사한 후 뜯어 고치는 전통적인 방법과 상속을 하는 방법과 근본적인 차이점이 없습니다. 그러나 코드의 유지, 보수 측면에서는 엄청난 차이가 있는데 원본을 변경해야 할 떄 복사한 경우는 양쪽을 다 직접 고쳐야 하지만 상속의 경우는 원본 클래스만 고치면 상속받은 클래스까지 한꺼번에 같이 수정되어 편리하며 불일치의 위험도 없습니다. 예를 들어 멤버 변수의 이름을 바꾼다거나 멤버 함수의 원형을 바꾼다고 할 때 원본의 멤버만 수정하면 됩니다.

A,B 멤버를 가진 Parent 클래스로부터 C멤버를 추가하여 Chile 클래스를 만들었다고 해 보겠습니다. 이 상태에서 A 멤버의 이름을 Alpha로 변경하고 싶을 때 복사해서 수정한 경우는 원본과 사본 두 군데를 고쳐야 하지만 상속을 받은 경우는 Parent의 A만 고치면 상속받는 Child는 더 이상 손데지 않아도 됩니다. 여러 단계로 재사용될 경우 이런 장점이 더욱 부각되는데 상속 단계가 5단계만 넘어도 엄청난 차이가 발생합니다. 상속이라는 개념은 어려운것도 아니고 이미 우리도 모르게 상속이라는 개념을 많이 활용해 왔습니다. C에서 기능의 단위는 함수인데 필요한 모든 함수들이 다 제공되는 것은 아니므로 원하는 기능을 추가하여 새로운 함수를 만들어 사용해야합니다. 예를 들어 문자열을 출력한 후 1초간 대기하는 함수가 필요하다면 다음과 같이 작성합니다.
1
2
3
4
5
void putsdelay(const char *message)
{
    puts(message);
    delay(1000);
}
cs
putsdelay 함수는 인수로 전달된 message 문자열을 puts 함수로 출력한 후 delay를 호출하여 1초간 시간을 지연시키는데 원래 puts 함수의 기능을 상송받아 대기하는 기능을 추가했다고 볼 수 있습니다. 원본함수인 puts를 뜯어 고친 것이 아니라 이 함수의 기능을 빌려 좀 더 구체적인 동작을 하는 특수한 함수를 정의한 것입니다. malloc은 항당 후 초기화를 하지 않는데 원한다면 상속받아서 memset을 추가하면 할당 직후에 원하는 값으로 초기화하는 allocandinit 따위의 함수를 만들 수도 있습니다. 이런 것이 개념적인 함수의 상속이며 기존 함수를 호출함으로써 간단하게 구현합니다. 이런 식으로 기존 함수를 한 번 감싸서 원래 동작에 약간 처리를 추가하는 함수를 래퍼(Wrapper)함수라고 하는데 원본 함수의 기능이 박뀌면 래퍼 함수의 기능도 덩달아 바뀝니다. 클래스의 상속도 이와 비슷하다고 생각하면 C++의 상속 개념을 대충 이해할 수 있을 것입니다. 물론 어디까지나 비유이므로 정확하게 같다고는 할 수 없습니다.


상속의 예


상속에 대한 문법적인 이론만 계속 나열해서는 이해하기 쉽지 않으므로 일단은 구체적인 실제 예를 보도록 하겠습니다. 다음 소스는 비록 극단적으로 간단하지만 상속을 통해 클래스를 재활용 하는 기본적인 방법을 보여 줍니다.

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include<iostream>
#include<Windows.h>
#include<conio.h>
void gotoxy(int x, int y)
{
    COORD CursorPosition = { x, y };
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), CursorPosition);
}
class Coord
{
protected:
    int x, y;
public:
    Coord(int ax, int ay)
    {
        x = ax;
        y = ay;
    }
    void GetXY(int &rx, int &ry) const
    {
        rx = x;
        ry = y;
    }
    void SetXY(int ax, int ay)
    {
        x = ax;
        y = ay;
    }
};
 
class Point :public Coord
{
protected:
    char ch;
public:
    Point(int ax, int ay, char ach) :Coord(ax, ay)
    {
        ch = ach;
    }
    void Show()
    {
        gotoxy(x, y);
        _putch(ch);
    }
    void Hide()
    {
        gotoxy(x, y);
        _putch(' ');
    }
};
 
void main()
{
    Point P(1010'@');
    P.Show();
}
cs

두 개의 클래스를 정의하고 있는데 Coord 클래스는 화면상의 좌표 하나를 표현합니다. 좌표는 위치만을 가지며 보이는 실체가 아니므로 크기나 모양, 생상 따위의 개념이 없습니다. 그래서 Coor 클래스에는 순수하게 위치만 표현할 수 있는 x,y만 멤버 변수로 선언되어 있습니다. 그리고 x,y를 액세스 하는 Get(Set)XY 멤버 함수와 생성자가 정의되어 있습니다.


두번째 클래스인 Point는 점을 표현하는데 눈에 보이는 점을 그리기 위해서는 좌표 외에도 실제로 화면에 출력할 때 어떤 문자로 출력할 것인지에 대한 정보가 필요합니다. 그래픽 환경이라면 점의 색상이 필요하겠지만 콘솔이므로 특정 문자를 출력함으로써 점을 대신 표현하기로 합니다. 이 특정 문자를 ch 멤보로 지정합니다. 이 외에 점을 관리하는 Show, Hide 멤버 함수가 정의되어 있는데 점은 화면에 보일 수도 있고 숨을 수도 있으므로 이 두 동장을 처리하는 멤버 함수가 반드시 필요합니다. 만약 이번 특성을 가지는 Point 클래스를 단독으로 정의한다면 아마도 다음과 같은 모양이 될 것입니다. 앞에서 썼던 Position 클래스와도 비슷합니다

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
30
31
32
33
34
35
 
 
class Point
{
protected:
    int x, y;
    char ch;
public:
    Point(int ax, int ay, char ach) :Coord(ax, ay)
    {
        x =  ax;
        y = ay;
        ch = ach;
    }
    void GetXY(int &rx, int &ry) const
    {
        rx = x;
        ry = y;
    }
    void SetXY(int ax, int ay)
    {
        x = ax;
        y = ay;
    }
    void Show()
    {
        gotoxy(x, y);
        _putch(ch);
    }
    void Hide()
    {
        gotoxy(x, y);
        _putch(' ');
    }
};
cs

이 선언문에서 보다시피 x,y와 Get(Set)XY멤버 함수는 좌표를 표현하는 Coor 클래스에 이미 정의되어 있는 것들입니다. 따라서 멤버를 새로 정의할 필요없이 Coord 클래스로부터 상속받으면 됩니다. 위 소스의 Point 클래스 선언문 뒤에 :public Coord라는 선언이 바로 Coord로부터 상속을 받으라는 뜻이며 컴파일러는 이 선언에 의해 Point 클래스에 Coord가 가진 멤버를 물려줍니다. Point는 Coord가 가진 좌표와 관련된 멤버는 그대로 사용하면서 여기에 점을 표시할 문자 ch 멤버와 자신을 보이거나 숨길 수 있는 Show, Hide 멤버 그리고 생성자만 추가하면 됩니다.


Point 클래스가 Coord 클래스로부터 상속받은 것입니다. 클래스끼리 상속될 떄 상위의 클래스를 기반 클래스(Base Class)라고 하며 상속을 받는 클래스를 파생 클래스(Derive Class)라고 합니다. 이 경우 Coord 기반 클래스로부터 Point 클래스가 파생되었다고 표현합니다. 기반, 파생이라는 용어 대신 부모, 자식이라는 용어를 대신 사용하기도 하고 상위 클래스(Super Class), 하위 클래스(Sub Class)라는 용어를 쓰기도 하는데 언어에 따라 사용하는 용어가 조금씩 다릅니다. 생성자, 파괴자 등의 특수한 몇 가지를 제외하고 파생 클래스는 기반 클래스의 모든 멤버를 상속받습니다. Point는 좌표에 대한 정보인 x, y 멤버 변수와 이 멤버에 대한 액세스 함수인 Get(Set)XY 멤버 함수를 정의하고 있지 않지만 기반 클래스인 Coord로부터 상속받았으며 그래서 Point에는 x, y 멤버가 정의되어 있는 것과 마찬가지입니다. Point의 멤버 함수인 Show, Hide에서 x,y좌표를 참조하여 점을 찍거나 숨길 위치를 결정하는데 아무런 문제가 없는 것입니다.


main에서 Point형의 객체 P를 선언하되 (10,10) whkvydp answk '@'으로 점을 표현하도록 했습니다. P,Show 함수를 호출하면 (10,10)좌표에 @ 문자가 출력됩니다. P는 상속에 의해 좌표에 대한 정보를 가질 수 있으며 이 좌표에 지정된 문자를 출력함으로써 자신의 존재를 나타낼 수 있는 완전한 객체인 것입니다.


상속과 정보 은폐


클래스가 상속될 떄 기반 클래스의 멤버에 대한 엑세스 속성이 파생 클래스에게 어떻게 상속되는지 다음 소스를 통해서 보겠습니다.

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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include<iostream>
using namespace std;
 
class B
{
private:
    int B_private;
    void B_fprivate(){ puts("private function in base Class"); }
protected:
    int B_protected;
    void  B_fprotected(){ puts("protected function in base Class"); }
public:
    int B_public;
    void B_fpublic(){ puts("public function in base Class"); }
};
 
class D : public B
{
private:
    int D_private;
    void D_fprivate(){ puts("private function in derived Class"); }
public:
    void D_fpublic()
    {
        D_private = 0//자신의 모든 멤버 엑세스 가능
        D_fprivate();
 
        B_fprivate(); // 부모 private 멤버에 엑세스 불가
        B_private = 0;
 
        B_fprotected();        //부모 protected 멤버에 엑세스 가능
        B_protected = 0;
 
        B_fpublic();        //부모 public 멤버에 엑세스 가능
        B_public = 0
    }
};
void main()
{
    D d;
 
    d.D_fpublic();        //자신의 멤버 함수 호출
    d.B_fpublic();        //부모의 멤버 함수 호출
}
cs

기반 클래스인 B에는 private, protected, public 각각의 엑세스 속성으로 멤버 변수와 멤버 함수를 모두 정의해 두었습니다. 멤버 이름과 소속과 엑세스 지정을 포함하여 쉽게 구분할 수 있게 작성했습니다. B에서 D를 파생했을 때 파생 클래스인 D에서 기반 클래스의 각 멤버들을 엑세스 하면 어떻게 될까요? 기반 클래스의 public 멤버는 공개되어 있으므로 파생 클래스뿐만 아니라 이 클래스의 외부에서도 얼마든지 액세스할 수 있습니다. D의 멤버 함수 D_fpublic에서 B_public과 b_fpublic은 얼마든지 액세스할 수 있으며 main 함수에서도 이 멤버들을 참조가 가능합니다. 그러나 기반 클래스의 private 멤버는 숨겨져 있으므로 외부에서와 마찬가지로 파생 클래스에서 직접 엑세스할 수 없습니다. 아무리 자식이라 하더라도 부모의 숨겨진 멤버를 건드리는 것을 허용하지 않습니다. 그래서 D::D_fpublic에서 B::B_private를 참조한다거나 B::b_fprivate멤버 함수를 호출하는 것은 에러로 처리됩니다. 이 두줄을 주석처리해야 컴파일이 됩니다.


상속관계에 있어서도 파생 클래스는 기반 클래스의 외부로 간주되어 엄격한 엑세스 제한이 적용됩니다. 그런데 파생 클래스는 기반 클래스와 어느 정도 관련이 있기 떄문에 때로는 파생 클래스에게 숨겨진 멤버에 대한 엑세스를 허용해야 할 경우도 있습니다. 클래스 외부와는 달리 쌩판 남은 아닌겁니다. 이럴 때 사용하는 액세스 지정이 protected이며 public과 private의 중간 정도에 해당합니다. protected로 지정된 멤버는 클래스 외부에서는 참조할 수 없지만 파생 클래스에서는 참조할 수 있는 엑세스 속성입니다.  위 소스에서 D::D_fpublic에서 부모의 protected 멤버인 B_protected, B_fproected는 엑세스가 가능합니다. 그러나 main에서 이 값을 참조하면 에러입니다. main은 명백한 클래스 외부이며 파생 클래스도 아니므로 이 멤버를 엑세스 할 수 업습니다. 엑세스 지정자의 기능을 표로 정리하면 다음과 같습니다.


 엑세스 지정자

 클래스 외부

 파생 클래스

 비고

 private

 엑세스 금지

 엑세스 금지

 무조건 금지

 protected

 엑세스 금지

 엑세스 허용

 파생 클래스만 허용

 public

 엑세스 허용

 엑세스 허용

 무조건 허용

protected 엑세스 속성은 상속 관계에 있지 않은 클래스나 외부에 대해 private와 같으며 파생 클래스에 대해서는 public과 같습니다. 외부에 대해서는 숨겨야 하지만 파생 클래스에서 액세스 할 필요가 있는 멤버는 proteced 엑세스 속성을 지정합니다. 파생 클래스는 기반 클래스와 아주 밀접한 관계에 있음에도 불구하고 기반 클래스의 private 멤버를 참조하지 못한다는 것은 선뜻 이해하기가 어려울 수 있지만 쓰지도 못할 멤버를 왜 상속받는지는 이해가 되지 않습니다. 하지만 부모 클래스가 스스로의 정보 은폐를 위해 자식에게조차 멤버를 숨겨야 할 필요는 분명히 있으며 이렇게 해야 파생 클래스가 영향을 받지 않습니다.


만약 부모의 private 멤버를 자식이 읽을 수 있다면 이는 정보 은폐를 완전히 포기하는 것과 마찬가지입니다. 왜냐하면 클래스가 아무리 정보를 감춰놓아도 외부에서 상속만 받으면 모든 멤버를 마음대로 건드릴 수 있기 때문입니다. private는 자식이 몰라도 되는 부분이며 마땅히 몰라야 하는 부분입니다. 파생 클래스는 기반 클래스의 private 멤버를 직접 읽지는 못하지만 기반 클래스의 public, protected 함수를 통해 이 멤버를 여전히 사용할 수는 있습니다. 다음 소스를 보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class B
{
private:
    int b;
public:    //public 말고 protected도 됨
    int Getb(){ return B; }
    void Setb(int ab){ b = ab; }
};
class D :public B
{
public:
    void function()
    {
        cout << "'b'in the base class = " << Getb() << endl;
 
    }
};
cs

D는 B의 private 멤버인 b를 직접 참조할 수는 없지만 상속받은 Get(Set)b 멤버 함수를 통해서 이 멤버값을 간접적으로 읽고 쓸 수 있습니다. 클래스 외부에서 적용되는 규칙이 파생 클래스에 대해서도 그대로 적용됨을 알 수 있습니다. 단 외부와는 달리 파생 클래스를 위해 protected라는 액세스 속성이 별도로 준비되어 있다는 점만 다릅니다. 일단 숨기되 차후에 상속될 가능성이 조금이라도 있다면 protected 엑세스 속성을 지정하는 것이 좋습니다.


상속 엑세스 지정


파생 클래스를 정의하는 일반적인 문법은 다음과 같습니다.

1
2
3
4
5
6
                        public
class 파생클래스 :  protected  기반클래스
                        private
{
        추가 멤버 선언
};
cs

클래스 선언문 다음에 : 이 오고 상속받을 기반 클래스의 이름이 옵니다. 그리고 :과 기반 클래스 이름 사이에 상속 엑세스 지정자라는 것이 위치하는데 이 지정자는 기반 클래스의 멤버들이 파생클래스로 상속될 때  엑세스 속성이 어떻게 변할 것인가를 지정합니다. 멤버의 엑세스 속성을 지정하는 public, protected, private와 똑같은 키워드를 사용하지만 의미는 다릅니다. 이 지정자에 따라 파생 클래스가 상속받는 멤버의 엑세스 지정이 어떻게 변하는지 표로 보겠습니다.


 상속 엑세스 지정자

 기반 클래스의 액세스 속성

 파생 클래스의 엑세스 속성

 public

 public

 public

 private

 엑세스 불가능

 protected

 protected

 private

 public

 private

 private

 엑세스 불가능

 protected

 private

 protected

 public

 protected

 private

 엑세스 불가능

 protected

 protected


먼저 기반 클래스의 privated 멤버는 어떤 경우라도 파생 클래스에서 읽을 수 없습니다. 따라서 private 멤버는 상속은 되지만 파생 클래스에서는 직접 참조할 수 없으므로 엑세스 속성이 아예 없다고 할 수 있습니다. 자신도 못 읽는 멤버에 대해 외부에서 이 멤버를 읽도록 허가하거나 금지하는 속성을 지정할 수는 없느 노릇입니다. 기반 클래스의 public, protected 멤버는 상속 엑세스 지정자에 따라 엑세스 속성이 변경됩니다. 상속 엑세스 지정자가 public 이면 기반 클래스의 엑세스 속성이 그대로 유지됩니다. 즉 부모의 protected 멤버는 상속된 후의 사식 클래스에서도 여전히 protected이며 부모의 public 멤버는 자실 클래스에서도 외부로 공개됩니다. public 상속은 부모로부터 상속받은 멤버의 엑세스 속성에 아무런 변화도 없는 상속입니다. 상속 엑세스 지정자가 private, protected 인 경우는 부모의 모든 멤버가 상속되면서 private, protected로 변경됩니다. 상속 엑세스가 지정자가 생략되면 디폴트인 private가 적용됩니다. 즉 다음 두 구문은 동일한 문장입니다.

1
2
class D : B
class D : private B
cs

클래스는 가급적이면 정보를 숨기려는 경향이 있기 때문인데 구조체의 경우 생략시 public이 적용됩니다. 통상 상속이라 하면 public 상속을 의미하며 나머지 두 가지 상속 액세스 지정자는 아주 특수한 목적에 사용됩니다. 이 두 가지 경우에 대해서는 나중에 하기로 하고 public 상속에 대해서만 고려하겠습니다.





'Programming > Cplusplus' 카테고리의 다른 글

네임스페이스(namespace)의 이용  (0) 2016.03.07
C++ - 클래스 상속(2/3)  (0) 2015.10.12
C++ - 연산자 오버로딩(3/3)  (0) 2015.09.21
C++ - 연산자 오버로딩(2/3)  (0) 2015.09.20
C++ - 연산자 오버로딩(1/3)  (1) 2015.09.20