===============================
블로그 : http://zeprod.org/
Project. Creaty : http://creaty.net/
===============================
혹시나 제 강의를 다른곳에 퍼가시려면 이 광고문구를 꼭 삽입해주세요.
보시는분도 별로 안계시지만 만약을 위해서랍니다. ㅋㅋ 그럼 시작하겠습니다.
오늘은 3편에서 말씀을 못드린 클래스에대한 설명을 간단히 추가하도록 하겠습니다.
분량면으로 보자면 '확장팩' 같은 개념이네요.
지난 3편에서 구조체라는 것을 말씀드렸습니다. 클래스는 바로 이 구조체를 기반으로 생겨난 C++의 새로운 개체입니다.
우선 클래스의 선언은 아래와 같습니다.
class A {
내부인자
}; // 선언
A a; // 사용법
전체적인 모양이 구조체와 비슷한 것을 알 수 있습니다. 모양뿐만아니라 사용법도 동일하게 할 수 있습니다.
class A {
public:
int a;
};
A a;
a.a = 0;
사용법이 동일하죠? 이때 구조체에선 볼수 없었던 public: 이라는 키워드가 추가된 것을 알 수 있습니다.
이 public: 키워드는 클래스의 내부인자의 접근 권한을 설정하는 것입니다.
이런 접근권한 설정 키워드는 대표적으로 public, private, protected 등이 있습니다.
public은 쉽게말해 구조체에서처럼 클래스 외부에서 접근이 가능하도록 설정하는 키워드입니다.
private는 클래스 내부에서만 접근이 됩니다. 이는 클래스에 자체적인 함수가 포함될 수 있으므로, 클래스 내부 함수에서만 접근이 가능하도록 설정하는 것입니다.
이처럼 C++은 클래스를 객체로 다루기위한 옵션이 들어있습니다. 이것은 C++이 객체지향 프로그래밍을 지원하기 위한 내용이므로 객체지향 프로그래밍이 무엇인지 잠시 설명드리도록 하겠습니다.
객체지향 프로그래밍이란, 프로그램 내부에 어떤 능력을 가진 객체를 만들어넣고, 그 객체를 이용해서 전체적인 프로그램을 만드는 것을 말합니다.
예를 들어 마을을 구현하는 게임을 만든다면, 케릭터라는 객체를 만들고 움직임, 행동 등을 만들어넣고, 케릭터 객체를 배열 등으로 여러개 선언하고 다양한 값으로 초기화 해 주는 것으로 쉽게 다양한 케릭터를 만들어낼 수 있게 되는데 이런 것을 객체지향 프로그래밍이라고 합니다.
이렇게 클래스에 능력을 만들어 넣으려면 어떻게 해야 할까요?
class A {
int a;
public:
A() { a = 0; }
~A();
int plus_num( int num );
private:
int plus() { return ++a; }
int minus() { return --a; }
};
int A::plus_num( int num ) {
if (num < 0) {
for (int i = 0; i < -num; i++)
this.minus();
}
else {
for (int i = 0; i < num; i++)
this.plus();
}
return a;
}
금방 코드가 길어졌네요. 지금부터 하나씩 설명해 드리겠습니다.
먼저 가장 큰 차이점으로는 class 내부에 함수가 선언되어있다는 것입니다.
plus()와 minus()는 아예 클래스 내부에 선언이 되어있고, plus_num()함수는 class 외부에 내용이 기록되어 있습니다.
이는 어떻게 하더라도 관계 없으나, 내용이 긴 함수의 경우에는 클래스 외부에서 선언하는 것이 컴파일러의 부담을 줄여주는 것으로 알려져 있습니다.
이렇게 함수를 선언해 놓으면, 아래와 같이 실제 변수로 선언한 뒤에 함수를 호출할 수가 있습니다.
A a;
a. plus_num(1);
이때 특이하게도 클래스와 동일한 이름을 가진 함수가 두가지 있습니다. A()/~A()입니다.
이는 클래스가 변수로 선언될때 초기화 / 정리하기 위한 내용을 담을 수 있습니다.
A()함수는 생성자라고 불리며 변수 선언직후에 호출되어 변수 초기화를 수행하게 됩니다. 이경우에는 A클래스의 a값을 0으로 초기화하는 내용이 담겨있습니다.
~A()함수는 파괴자라고 불리며 변수로서 존재할 필요가 없어져 메모리에서 삭제되기 직전에 호출되는 함수입니다. 이경우 포인터를 이용한 메모리 동적할당이 이루어진 경우라면, 이 파괴자에서 호출한 메모리를 예약해제해주어야 합니다.
이런 생성자와 파괴자 함수는 선언 / 파괴시 시스템에서 호출할 수 있도록 반드시 public: 키워드로 선언되어야 정상작동하게 됩니다.
private 등으로 선언한다면 시스템에서는 함수를 찾지못하고 에러를 낼 것입니다.
이처럼 private로 선언된 함수는 외부에서는 절대로 사용할 수 없습니다.
예에서 사용된 A클래스에서는 plus(), minus()함수가 private로 선언되어 있으며, 같은 A클래스 내의 함수인 plus_num() 함수에서 자유롭게 사용되고 있는 것을 알 수 있습니다.
이때 this.plus() 또는 this.minus() 등으로 this라는게 앞에 붙어있는데, 이것은 자기자신 객체를 말하는 것으로 생략하여도 무방합니다.
단지 아래와 같이 사용한다면 함수를 찾을 수 없다는 오류나 나타날 것입니다.
A a;
a.minus(); // 에러 : a.minus() 함수를 찾을 수 없습니다.
이렇게 클래스별로 자체적으로 사용할 기능과 외부에 노출할 기능을 설정할 수 있는 것으로 내부 소스에 대한 의존도를 줄일 수 있습니다.
이렇게 함수가 포함된 클래스는 각 기능을 독립시키는데 아주 중요한 역할을 합니다.
예를들어 앞서 설명했던 NPC 마을구현을 살펴보자면, 아래와 같이 편리하게 수행이 가능합니다.
Character a[100]; // 캐릭터 객체 100개 선언
for (int i=0; i<100; i++)
a[i].load("npc_"<<".txt") // npc_1.txt ~ npc_100.txt까지 설정을 읽어냄
~ 렌더링 루프 ~
for (int i=0; i<100; i++) {
a[i].animate(); // 한 프레임당 동작실행
a[i].render(); // 화면에 표시
}
위와 같이 공통된 함수와 다양한 속성변화를 통해 마을주민 100명을 만들어내는 것을 보았습니다. 참 편리해보이죠?
이렇게 능력을 갖춘 클래스들을 활용하기위한 다양한 방법들이 있습니다. 바로 상속입니다.
상속은 객체지향 프로그래밍을 하면서 편리하게 다양한 클래스들을 분화시키면서 만들어낼 수 있는 기반시스템을 제공합니다.
class B : public A
{
public:
B();
~B();
int mul(int num) {
int b = this.a;
for (int i = 0; i < num; i++)
this.plus_num(b);
}
}
이번엔 B객체를 선언하고 뒤에 : public A라는 문구를 붙였습니다. 이것은 B클래스는 A클래스의 모든 내용을 public으로 상속받는다 라는 의미입니다. 상속이란 상대 클래스의 모든 기능과 내부 변수등을 똑같이 사용하겠다는 것입니다.
따라서 a변수나 plus_num함수를 사용할 수 있게 되는 것입니다. private로 선언된 함수들도 상속 키워드에따라 B클래스에서는 public으로 변환되어 사용되므로 모두 사용이 가능합니다.
이런 상속은 비슷하지만 조금씩 다른 클래스들을 만들때 아주 편리하게 사용됩니다.
예를들어 상단의 케릭터 객체를 상속받아 몬스터객체를 만들고 전투관련 기능을 추가한다면 캐릭터 객체에서 이미 구현했던 내용들이 아주 쓸모있게 사용될 것입니다.
그렇다면, private로 선언된 내용을 다른 특정 클래스에서만 사용할 수 있도록 허락해주려면 어떻게 해야할까요?
그것이 바로 friend 키워드입니다. 사용방법은 friend class B;으로 B클래스에서 자신의 private 인자를 사용하는 것을 허가하는 것입니다.
또한 함수 구현방식을 조율하는 키워드로는 virtual 키워드가 있습니다. 이것은 함수 선언시 앞에 붙여주게 되는데, 이렇게 virtual로 선언된 함수는 그 클래스를 상속받았을때, 함수내용을 덮어 쓸 수 있도록 가상함수를 형식상 만들어 놓는 것입니다.
지금까지 클래스에 대하여 알아보았습니다. 사실 3편에 있어야 하는 내용을 추가하는 것으로 분량이 만족스럽진 않지만, 이렇게 완결을 내고 끝을 내겠습니다.
다음시간에는 함수포인터에 대한 내용을 설명하겠습니다. 다음에 뵙죠.