4.상속,폴리모피즘,오버로딩

본장에서는 클래스의 두 번째 특징에 해당하는 상속에 대한 내용을 다룹니다. 본장까지 공부를 하게 되면 C++이 어떤 형태인가라는 개념을 파악하실수 있을겁니다. 그만큼 본장은 C++의 중요 내용을 다루고 있습니다. 실제 MFC를 사용할 경우 본장까지의 내용만 알아도 충분히 이용할수 있습니다.

1.상속

□사람은 동물의 기능을 상속받고 동물은 생물의 기능을 상속받고....□1장에서 설명한 내용입니다. 특정 클래스를 만들고 이클래스에 또다른 기능을 넣어서 새로운 클래스를 만들고자 할 경우 이때 상속의 개념을 사용합니다.

예를 들어서 CWnd라는 윈도우를 출력하는 하나의 클래스가 있다고 가정을 하고 이클래스를 상속받아서 두꺼운 프레임을 그리느 또하나의 윈도우를 만들고자 한다면 다음과 같이 할수 있습니다.

class CFrame:public CWnd

{

:

};

클래스 명다음에 ':□ 문자와 함께 public를 설정하고 상속받고자 하는 클래스를 써주면 설정된 클래스는 public다음에 기록한 크래스를 상속받게 됩니다.

위와 같이 하였을 경우 CWnd에 있는 모든 맴버함수와 맴버 변수를 CFrame에서 핸들링할 수가 있습니다. CWnd에 다음과 같은 함수가 있다고 가정을 하면

class CWnd{

protected:

int x;

int y;

int cx;

int cy;

int color;

public:

void SetColor(int c);

void SetPos(int x1,int y1,int x2,int y2);

void Draw();

};

CWnd를 상속받은 CFrameWnd CWnd의 맴버 x,y,cx,cy,color를 자신의 맴버 변수로 사용할수 있으며 SetColor,SetPos,Draw 등을 자신의 함수처럼 그대로 이용할수 있습니다.

ex)

CFrameWnd *pWnd= new CFrameWnd;

pWnd->SetPos(0,0,100,100);

pWnd->Draw();

CWnd는 윈도우에서 기본적인 윈도우를 그리는 형이고 CFrameWnd는 윈도우의 프레임 형태를 표현하는 윈도우라고 가정을 합시다.윈도우 프레임에는 메뉴가 설정될수 있기 때문에 CFrameWnd는 CWnd와 달리 또하나의 맴버가 설정될것입니다.

class CFrameWnd: public CWnd

{

protected:

char manu[80];

public:

void SetMenu(char *data);

};

CFrameWnd는 CWnd의 모든기능을 포함하며 mane라는 맴버 변수를 설정하고 SetManu라는 또하나의 함수를 설정하였습니다.

최초에 만든 클래스에 기능이 단순하다 하여 그 클래스를 수정할 필요없이 그 클래스를 상속받은 새로운 클래스를 만든다는 것이 상속의 목적입니다. 보통 프로그램을 제작할 경우 프로그램에 필요한 기본기능을 가진 최초의 객체를 설정하고 (MFC에서는 CObject로 설정하였습니다) 이 객체로부터 계속적으로 새로운 클래스를 파생시킵니다. 이런식으로 파생을 한형태를 보면 일반적으로 트리구조를 갖는 형태가 됩니다. 상속의기능이 막강한 것 만큼 프로그래밍의 방법에도 변화가 있습니다. 지금까지는 프로그램을 작성하면서 □아차 이게 아닌가보다!□ 그러면 필요 부분을 수정을 해나가는 방법을 제시하였으나 C++로 객체 지향적인 프로그램을 작성한다면 프로그램을 작성하면서 수정하는 것이 아니라 먼저 클래스이 계층적 구조를 설정한다음 이것에 의해서 프로그래밍하는 것이 일반적입니다. 따라서 C++로 프로그래밍을 할대는 코딩을 하는것보다 프로그래밍 구조를 설계하는 것이 더욱 중요하게 됩니다. 윈도우에서 C++ 로 프로그래밍할 때 이런 문제점의 해결책을 제안한 것이 바로 MFC입니다. 이것은 Microsoft Foundation Class로 윈도우에서 프로그래밍 하는데 필요한 기본 클래스를 계층적으로 구성한것입니다. 따라서 MFC로 프로그래밍을 한다면 구조설계라는 어려운 부분을 해결하게 되며 C++의 기능만 알고 있어도 간단하게 프로그래밍에 접근할수 있는것입니다. 보통 Visual C++로 프로그래밍한다고 하면 일반적으로 MFC를 이용한다고 생각을 하는 이유가 바로 이것에 기인합니다.

상속을 할수 없는경우

상속을 하였다 하더라도 상위 클래스로부터 상속받지 못하게 할 경우 선언을 private로 해주시면 됩니다. 다음은 private를 설정한 예입니다.

class CData{

private:

char buff[80];

protected:

char mybuff[80];

public:

void CData()

:

};

class CListData:public CData

{

:

}

위와 같이 CData를 상속받은 CListData라는 클래스가 있을 경우 mybuff와 public에 설정한 CData()외에 모든 함수들은 CListData에서 사용을 할 수가 있습니다. 그러나 CListData에서 CData안에 있는 buff라는 데이터를 상속 받을수 없습니다. private 로 선언하면 그 맴버는 그클래스에 만 한하게 됩니다. private 로 설정한 데이터는 파생된 클래스에서는 사용할수 없을 뿐만아니라 존재하지 않는 것으로 생각하여 에러를 낼것입니다.

CListData::CListData()

{

strcpy(buff,"test");//<-- 컴파일시 에러가 발생

}

상속 예제 CpEx06

CpEx06은 CWnd라는 윈도우 클래스를 만들고 이것을 상속받은 CFrameWnd라는 클래스를 만든 예입니다. CWnd나 CFrameWnd는 윈도우를 그리는 것이 아니라 다만 윈도우 정보를 텍스트로 출력할 뿐입니다. 본예제는 상속이 어떤 것이며 어떤한 의미인가를 설명하기 위한 예제입니다. 본예제에서는 CWnd와 CFrameWnd 클래스를 정의한 파일을 분리하였습니다. CWnd와 CFrameWnd의 클래스는 CWnd.h에 이함수들의 실제 내용은 CWnd.cpp에 설정하였으며 CpEx06.c에는 main()함수만 기록하였습니다.

(프로그래밍 소스)

//CWnd.h

//Cwnd 와 CFrameWnd클래스 헤더

class CWnd{

protected:

int x;

int y;

int cx;

int cy;

int color;

public:

void SetColor(int c);

void SetPos(int x1,int y1,int x2,int y2);

void Draw();

};

class CFrameWnd:public CWnd

{

protected:

char manu[80];

public:

void SetMenu(char *data);

void PrintMenu();

};

/////////////////////////////////////////////////////

//CWnd.cpp

#include <stdio.h>

#include <string.h>

#include "CWnd.h"

void CWnd::Draw()

{

printf("%d %d %d %d 에 %d 색으로 윈도우 그림\n",x,y,cx,cy,color);

}

void CWnd::SetPos(int x1, int y1, int x2, int y2)

{

x=x1;

y=y1;

cx=x2;

cy=y2;

}

void CWnd::SetColor(int c)

{

color=c;

}

void CFrameWnd::SetMenu(char *data)

{

strcpy(manu,data);

}

void CFrameWnd::PrintMenu()

{

printf("메뉴명:%s\n",manu);

}

///////////////////////////////////////////////////////////

//CpEx06.cpp 메인 프로그램

#include <stdio.h>

#include "CWnd.h"

void main()

{

char temp[80];

int x,y,cx,cy,color;

CFrameWnd *pWnd= new CFrameWnd;

printf("프레임을 그릴 좌표:");

gets(temp);

sscanf(temp,"%d %d %d %d",&x,&y,&cx,&cy);

pWnd->SetPos(x,y,cx,cy);

printf("색번호:");

gets(temp);

sscanf(temp,"%d",&color);

pWnd->SetColor(color);

pWnd->Draw();

pWnd->SetMenu("프레임 메뉴");

pWnd->PrintMenu();

delete pWnd;

}

(프로그램 소스끝)

(그림 1) CpEx06 출력 결과

2.폴리모피즘 (오버라이딩)

클래스에서 생성자를 설정하고자 할 경우 때로는 인자를 주소 설정하고 때로는 인자없이 설정하고자 할경우가 있습니다. 이때 사용할수 있는 것이 오버라이딩 기법입니다. 다음은 클래스 생성자의 오버라이딩 예입니다.

class CString

{

protected:

char buff[80];

public:

CString CString();

CString CString(char *data);

};

CString::CString(char *data)

{

strcpy(buff,data);

}

CString::CString()

{

strcpy(buff,"nodata");

}

위의 예를 본다면 CString 라는 클래스에는 생성자가 두 개입니다. 하나는 인자가 없는것이며 또하나는 char *data라는 인자가 있는 것입니다. C에서는 위와 같이 같은 함수를 인자를 달리 같게 쓴다면 에러가 납니다. 그러나 C++에서는 같은 함수라도 인자를 달리하여 사용할 수가 있습니다.

만일 생성자를 다음과 같이 호출한다고 하면

CString *m_string;

m_string = new CString;

위의 생성자는 CString에서 CString()함수를 수행하게 됩니다. 결국 CString의 buff안에는 "nodata"라는 값이 설정됩니다.

또하나의 예로 다음은 CString에서 CString CString(char *data); 수행한 예입니다.

CString *m_string;

m_string = new CString("홍길동");

위와 같이하였다면 m_string의 buff안에는 "홍길동□ 이라는 문자열이 들어가게 됩니다. 오버라이딩이란 바로 같은 함수를 인자를 달리해서 여러개을 만들경우에 사용하는 것을 말합니다. 위의 예에서 buff라는 문자열 버퍼를 만들었지만 우리는 이버퍼에 숫자도 넣고자 할 때도 있고 또는 문자를 넣고자 할경우도 있습니다.

이럴 경우 C에서는 보통 다음과 같은 방법을 이용하였습니다.

SetDataString(char *data);

SetDataInt(int data);

목적은 데이터를 설정하고자 하는 것인데 설정하는 인자를 달리할때마다 새로운 함수가 늘어나서 복잡하였으나 C++에서는 이것을 한 개의 함수로 설정할수 있게 되어 매우 간단하게 설정할수 있습니다.

다음은 위의 CString클래스에서 SetData함수를 오버라이딩으로 정수형데이터를 설정하는 함수와 문자열 데이터를 설정하는 함수를 만든 예입니다.

class CString

{

protected:

char buff[80];

public:

void CString SetData(char *data);

void CString SetData(int data);

};

void CString::SetData(char *data)

{

strcpy(buff,data);

}

void CString::SetData(int data);

{

sprintf(buff,"%d",data);

}

3. 오버로딩

전장에서 연산자 오버로딩에 대해서 배웠습니다. 연산자 오버로딩은 기존의 연산자들의 기능을 무시하고 자신의 기능을 사용하자는 것입니다. 함수 자체도 오버로딩이 가능합니다. 예를 들어 CpEx07에서 사용한 CWnd를 상속받은 CFrameWnd에서 Draw함수가 적당하지 않다고 가정을 합니다. Draw함수에는 □윈도우 그림□ 이라는 문자가 출력되었는데 CFrameWnd에서는 □프레임 윈도우 출력□이라는 문자열이 출력되었으면 할경우가 있을것입니다. 이때 상속받은 클래스이 맴버 함수를 사용하지 않고 자신의 함수로 대치할수 있습니다.

class CWnd

{

:

public:

void Draw();

:

};

class CFrameWnd:public CWnd

{

:

public:

void Draw();//CWnd의 Draw를 사용하지 않고 자신의 함수를 사용

};

void CFrameWnd::Draw()

{

printf("프레임 윈도우 출력□);

}

상속을 받은 함수를 사용하지 않고자 할 경우 그함수와 같은 형을 자신의 클래스에 설정하고 함수명 내용을 코딩하여 주면됩니다.

이런 오버라이딩은 연산자 오버라이딩과 의미가 비슷합니다. 다만 함수 오버라이드는 상속받은 함수에서 그함수를 자신만의 형태로 사용하는 의미이고 연산자 오버라딩은 C에서 사용되는 일반적인 방법을 무시하고 자신만의 연산을 한다는 의미입니다.

오버로딩 예제 CpEx07

CpEx07은 CpEx06에서 CWnd의 Draw함수를 CFrameWnd에서 오버라이딩한 예제입니다.

(프로그램 소스시작)

//CWnd.h

//Cwnd 와 CFrameWnd클래스 헤더

class CWnd{

private:

char buff[80];

protected:

int x;

int y;

int cx;

int cy;

int color;

public:

void SetColor(int c);

void SetPos(int x1,int y1,int x2,int y2);

void Draw();

};

class CFrameWnd:public CWnd

{

protected:

char manu[80];

public:

void SetMenu(char *data);

void PrintMenu();

void Draw();

};

/////////////////////////////////////////////////////

//CWnd.cpp

#include <stdio.h>

#include <string.h>

#include "CWnd.h"

void CWnd::Draw()

{

printf("%d %d %d %d 에 %d 색으로 윈도우 그림\n",x,y,cx,cy,color);

}

void CWnd::SetPos(int x1, int y1, int x2, int y2)

{

x=x1;

y=y1;

cx=x2;

cy=y2;

}

void CWnd::SetColor(int c)

{

color=c;

}

void CFrameWnd::SetMenu(char *data)

{

strcpy(buff,"test");//<--이부분은 에러 private로 선언했음

strcpy(manu,data);

}

void CFrameWnd::PrintMenu()

{

printf("메뉴명:%s\n",manu);

}

void CFrameWnd::Draw()

{

printf("%d %d %d %d 에 %d 색으로 프레임 윈도우 출력\n",x,y,cx,cy,color);

}

///////////////////////////////////////////////////////////

//CpEx07.cpp

#include <stdio.h>

#include "CWnd.h"

void main()

{

char temp[80];

int x,y,cx,cy,color;

CFrameWnd *pWnd= new CFrameWnd;

printf("프레임을 그릴 좌표:");

gets(temp);

sscanf(temp,"%d %d %d %d",&x,&y,&cx,&cy);

pWnd->SetPos(x,y,cx,cy);

printf("색번호:");

gets(temp);

sscanf(temp,"%d",&color);

pWnd->SetColor(color);

pWnd->Draw();

pWnd->SetMenu("프레임 메뉴");

pWnd->PrintMenu();

delete pWnd;

}

(프로그램 소스끝)

CpEx07에서 CWnd.cpp안에 void CFrameWnd::SetMenu(char *data)

첫줄 때문에 컴파일시 에러가 납니다. 이유는 CWnd에 private로 설정한 buff를 CFrameWnd에서 접근하고자 했기 때문입니다. CFrameWnd::SetMenu의 첫 번째 줄을 삭제하면 컴파일에 이상이 없을것입니다.

(그림 2) CpEx07 출력 결과