Chapter 4
4. OOP Basics
4.1 Object-Oriented Programming
The OOP paradigm is based on the human way to think of things, that each object in the known universe has within itself its properties and its potential, that they are mostly self-contained entities. This is also using in programming, since programs are often required to deal with a lot of components of different kinds, where it can be an advantage to let each of these become a self-contained object that can largely take care of itself without depending on the main program to know how to do everything the object has to do (encapsulation). This is especially useful in game programming, since every detail in the game can be taken care of by its own encapsulated OOP object quite independently from other objects. This lets each other has its own mind independent from the others, and relieves the main game loop from having to know anything about what the objects are actually doing.
OOP(Object Orinted Programming) 패러다임은 인간이 생각하는 것에 기반을 두었다. 이게 뭔말이냐면 사람들은 이 세상 모든것이 각각의 속성이나 (보이지 않는)잠재성을 가지고 있다고 생각한다는 것이다. 이것은 프로그래밍 세계에서도 동일한데, 프로그램들이 다양한 외부 컴포넌트를 필요로 하는것 보다, 오브젝트가 자신이 필요로 하는 것들을 내부적으로 포함하고 있다면(캡슐화) 훨씬 이득이 생긴다는 것이다. (불필요한 인터페이스를 노출하지 않는것과 외부 의존성이 상당히 낮아지는것을 의미한다.이것은 즉, 프로그래밍 복잡도를 화아악! 끌어 내릴수 있다는 이야기다.) 이건 말이지 특별히 게임 프로그래밍에서 꽤 쓸만한 것이란 말이지. 게임에서 각 오브젝트들은 자신이 할 역할에 충실하게 되고 게임의 루프를 줄여줄수도 있거등.(으윽, 발 의역..T^T)
So, what we are going to do is to write an OOP backbone that we can base our game objects on, and then use it to write an action game. We will need to make an object base class, and find some way of managing all the independent objects so that they can be used. The latter is where STL comes in. But first, we need to know to make a basic object class.
그래서, 우리가 할일은 OOP를 사용해서 우리 게임의 뼈대를 구성하고, 그것을 이용해서 액션게임을 만들거라는 거다.우리는 기반(base) 클래스를 만들어야 하고, 모든 독립적인 객체를 관리할수 있는 방법을 찾아야 한다. 그래야 우리가 걔네를 사용할수 있으니까 :) 나중에 STL을 쓰긴 할테니 걱정은 접으시고.. 우선 기반(base) 클래스 만드는법이나 알아보자고.
4.2 C++ Classes
The C++ programming language has much to offer when it comes to OOP programming. (Of course, there might be better languages when it comes to OOP, but only C++ is (mostly) compatible with the widely-used C, in which Allegro was written.) We will start with a basic class definition for our helicopter object.
C++은 OOP 프로그래밍 하기에 필요한것들을 넘치도록 제공해 준다. (아, 뭐 물론 OOP 에 훨씬 적절한 언어들도 쌔고 쌨지만, C++만이 현재 가장 대중적으로 쓰이-였었지-는 C와 호환을 제공하니까;; 게다가 알레그로는 C로 작성되었다능) 우리는 우리의 헬리콥터 클래스를 만드는 것으로 부터 공부를 시작하겠다.
In many ways, this is a lot like a struct, and can be used in much the same way:
아무리봐도, 이건 구조체랑 무진장 흡사하다. 보통 아래처럼 쓰인다.
but there is in fact a difference, as you would see if you tried to do
자자, 내가 앞에서 무진장 비슷하다고 했잖아? 근데, 이제 다른걸 알려줄께.
The Hero object is an instance of the chopper class, and not just a plain structure, thus it would not work.
Hero 객체는 chopper 클래스의 인스턴스야, 한마디로 걍 구조체랑은 틀린거란 말이지(구조체 <-> 클래스 이름부터 틀리잖냐). 그래서 뭔말이 하고 싶은거냐면, 위와 같이 초기화 하면 컴파일 안된다- _-
What the public clause means is that whatever is defined in the class (or struct) after it, will be accessible to code outside the class definition as well, which is necessary for parts of the class that isn't self-contained. Since this class isn't self-contained at all yet, both fields (X and Y) have to be marked public.
그리고, public 이라는 녀석은 클래스(구조체에서도 public 쓸수 있음여)가 선언된 다음에 클래스 외부에서 엑세스 가능함을 의미한다. 한마디로 캡슐화가 안되었다는 말이지염. 웅.., 설마 public을 빼먹은 사람이 있다면 당장 x, y 앞에 public이라고 적어주도록 하자굿.
As a first step towards making the object self-contained, we will now move the drawing and input code into it.
한발자국 더 나가서, 드로잉이랑 입력 코드를 클래스 안에다가 박아보자.
This demonstrates two ways of defining a method. Both chopper::draw() and chopper::input() are methods. Methods are much like functions, except that they operate on an object. chopper::draw() is declared and defined inline, this is often useful to avoid unnecessary method call overhead.
이 예에서는 메쏘드(멤버 함수)를 선언하는 두가지 방법을 보여준다. chopper::draw() 랑 chopper::input() 요녀석이 메쏘드다. 메쏘드는 걍 함수다. 다만, 메쏘드는 동작(operate)의 대상이 오브젝트라는 차이가 있다. chopper::draw() 는 inline으로 선언되었다. 이것은 불필요한 함수 호출 오버헤드를 피할수 있다. (하지만.. 오버헤드가 얼마나 된다고.. 쯥;)
4.3 Constructors
Clearly, we've come a long way towards the encapsulation we want, since the main game loop doesn't have to bother about the coordinates within the object, but still we need to set the initial position. So we should write a method for setting the initial position, but for this we will use a special method, the constructor. The constructor of an object is always called when it is created, and similarly there is a destructor that will be called when it is destroyed. C++ automatically constructs an object when you define a variable of the object type, and destroys it when it goes out of scope, so it's not necessary to do this manually for automatic variables, like our variable Hero.
확실히, 우리는 우리가 바라는대로 캡슐화를 하기 위해서는 갈길이 멀다. 우리가 바라는 캡슐화는 뭐, 게임 루프에서 클래스 내부를 직접 건드릴 필요가 없도록 하는것 정도인데, 그래도 얘가 처음에 나타날 장소 정도는 정해야 할것 같다. 그러니, 우리는 초기화 하는 메쏘드를 만들어야 한다. 근데, 이 초기화를 위해서 우리는 '생성자' 라는 특별한 메쏘드를 작성할 것이다. '생성자'는 객체가 생성될때 불리는 함수이고, 이와 비슷하게 객체가 삭제될때는 '소멸자' 라는 메쏘드가 불린다. C+에서는 객체 타입의 변수(클래스 변수)가 생성될때와 그 변수의 유효 범위를 벗어날때 생성자와 소멸자가 자동으로 불려지게 된다. 따라서 automatic 변수(우리의 Hero와 같이 일반적인 클래스 변수) 에서 우리가 직접 생성자나 소멸자를 호출하는 캐 귀찮은 짓을 하지 않아도 된다는 말이다.
We will define two constructors, the default constructor that sets a default initial position, and a constructor where it is possible to specify the initial position explicitly. C++ allows several functions, methods, and operators with the same name but with different parameter lists to coexist, a feature called "overloading", where the compiler automatically chooses the right function according to the given parameters. We will use this feature to have two different constructors. Change the class definition to read:
우리는 생성자를 두개를 만들거다. 우선 첫번째는 고정된 초기화 값으로 초기화 하는 생성자를 만들것이고 두번째 생성자는 원하는 초기화 값을 설정할수 있도록 할것이다. C++은 동일한 이름에 변수값만 다른 함수들, 메쏘드들, 동작들을 할수 있도록 해 놓았는데, 이를 '오버로딩'이라고 한다. 얘(오버로딩 기능)는 이름과 매개변수들로 원하는 함수를 정확히 찾아서 호출한다. 우리는 이 기능(...)을 이용해서 두개의 다른 생성자(생성자 이름은 동일하겠지...)를 만들것이다. 다음을 보자.
Then remove the line
그리고 다음 줄을 지우자.
and try it. The object is now completely encapsulated, the main loop doesn't know anything about X or Y. We have therefore taken them away from under the public clause; they are thus now private to the object, and hidden from everything else. Note that the helicopter now starts at the top left of the screen, because we put the default position in the default constructor to be 0,0. To try the alternate constructor, replace
그리고 시도(try) 해보자. (뭘0,.0?). 이제 그 오브젝트(Hero)는 메인루프에서 X,Y 라는것에 대해 전혀 모를만큼 완전히 캡슐화 되었다. 우리는 걔네(X,Y)를 private로 옮겨버렸기 때문에, 다른 모든것으로부터 감춰진 것이다. 참고, 헬리콥터는 좌 상단에서부터 출력되는데, 우리가 기본 생성자에서의 좌표를 0,0으로 설정했기 때문이다. 만약 초기화 좌표를 바꾸고 싶다면 다음과 같이 수정하면 된다.
with
This will still define the variable Hero to be of the data type chopper, but since chopper is a class, its constructor must be called when it is created. This means that we will call the constructor that takes two integer values and use it, giving the values 50 and 100, instead of using the default constructor. This alternate constructor happens to set the initial position to the given values.
Hero 변수는 여전히 chopper 타입의 변수일 뿐이지만, chopper는 클래스 이기 때문에 생성자는 변수가 생성될때 호출된다. (그래서 chopper Hero 시점에 (50, 100) 처럼 -생성자에-인자를 넘기는 거지.) 이 말인 즉슨, 위 내용을 보면, 클래스 변수인 Hero가 초기화 될때 기본 생성자를 호출하도록 되어 있었는데, 우리가 50,100 을 매개변수로 받는 생성자를 호출하도록 변경했다는 말이 되겠습니다. 이 alternate(대안...;) 생성자는 매개변수로 받은 값으로 초기 위치를 지정하도록 되어있다.
4.4 Inheritance
One of the most important advantages OOP has over traditional procedural programming, is the concept of inheritance. This means that it is possible to write a base class with all the basic and abstract functionality, variables, and common interface you need, and then write derived classes that inherits everything the base class has, and adds its own functionality. Not only can this reduce the amount of coding by letting any derived class use the base class's functionality when it needs to, thus simplifying implementation of similar classes and structures, but there is also another very important implication: the derived class is compatible with the base class, and can be used in place of the base class. This allows a system to override any of the base class functionality it wants, and use the new objects where the base class would be used, thus adapting it to its own needs. For us, this is very important; we can make an array of pointers to base class objects, and go through this array at any time, calling the methods of its elements. Depending on which derived class is present in this array, the individual objects can respond in completely different ways to each other, without the calling code having to know anything about it.
전통적인 프로그래밍 언어들(C라던가 아니면 C라던가..;)에 비해 OOP 의 중요한 이점중에 하나는 상속이라는 컨셉이다. 상속이라는게 뭐냐면 말이야. 베이스 클래스에서 기본적이고 추상적인 함수라던지 변수라던지 인터페이스라던지를 만들어 놓고, 자식 클래스에서 베이스 클래스의 내용을 다 가져오면서도 추가적으로 자신이 필요로 하는 기능을 더할수 있다는 것이지. 이거는 말이야 코딩량을 줄여준다는 잇점뿐 아니라, 베이스 클래스의 기능이 필요할때 쓸수있고, 그로인해 비슷비슷한 클래스나 구조체를 구현하기 간단해 진다는 장점이 있다는 말이지영. 또 중요한거 한개: 자식(derived) 클래스는 베이스 클래스랑 호환되기 때문에 베이스 클래스가 쓰여야 할곳에서 자식 클래스가 대신 쓰일수도 있다는 것이다. 한마디로, 베이스 클래스에다가 필요로 하는 기능을 덧 붙일수도 있는거고(자식 클래스), 필요하다면 베이스 클래스가 쓰여야 할곳에 자식클래스를 대신 사용할수도 있다는 이야기다. 요거요거요거는 우리한테 무진장 중요한거다: 우리는 베이스 클래스 객체의 포인터 배열을 만들수 있고, 언제라도 이 배열을 통해서 필요한 메쏘드를 부를수 있다. 이 (포인터)배열에서 자식 클래스에 따라 각기 다른 응답을 하도록 할 수 있다. (음.. 발번역이라 내용이 많이 헷갈리는데, 이거 업캐스팅이랑 다운캐스팅 이야기 하는거 아닐려나...- .-;)
So, what we are going to do is to write a base class that we can use as a foundation for our game elements. We will also use the Allegro-defined fix data type here, to enable sprites to move a fractional amount of pixels per frame. This will enable objects to move slower than one pixel per frame without standing totally still. Also, we won't bother to have a default constructor.
따라서, 이제 우리가 해야할일은 우리의 게임에서 쓰일 element의 베이스 클래스를 작성하는 것이다. 우리는 각 프레임에서 스프라이트 처리를 위해서 Allegro에서 (미리)선언되어 있는 fix 데이터 타입을 사용할 것이다. 이것은 한 프레임당 픽셀 하나 찍는것 보다는 느리게 동작할 것이다.(...이거 맞는 번역임?) 그리고, 우리는 기본 생성자 코드를건드리지는작성하지 않을것이다.
The protected clause is similar to private, except that it allows derived classes to access the elements defined protected. We have now also declared the methods virtual, which means that they are encoded into the object structure in such a way that they can be overridden transparently by derived classes, even if the code that uses the object thinks it's an instance the base class.
protected 는 private와 비슷하다, 다만, 자식 클래스에서 액세스 가능하다는 차이가 있다. virtual 이라는 키워드를 포함하는 함수도 볼수가 있는데, 이것은 자식클래스에서 투명하게 override 가능하도록 한다. 다시 말해 자식 클래스를 참조(& or *)하는 베이스 클래스의 인스턴스가 virtual 함수를 호출하면 자식 클래스에서 선언된(오버라이드) 함수를 콜함. (http://www.hybrid.pe.kr/tt/305 참고)
When we use this base class, this is how our program will look like.
이 베이스 클래스를 사용하면, 우리의 프로그램이 다음과 같이 바뀐다능.
As you can see, you use colons to inherit a base class, as well as to use an inherited constructor. The base class is inherited public to make public members of the base class remain public, and not turned private.
보다시피, 베이스 클래스에서 상속받기 위해서는 : 을 쓰면 된다. 베이스 클래스에서 public이었던 것들은 상속받은 후에도 public으로 남아 있으니, 상속 받으면 private로 변한다거나.. 하면서 두려움에 떨지 않아도 된다.-.-;
To learn more, proceed to the next chapter
음.. 내가 번역을 하고 있긴 하지만, 나같아도 원어로 보겠다 싶은 수준의 발번역..;; 어쨌든 다음 챕터로 궈궈궛-0-
4. OOP Basics
4.1 Object-Oriented Programming
The OOP paradigm is based on the human way to think of things, that each object in the known universe has within itself its properties and its potential, that they are mostly self-contained entities. This is also using in programming, since programs are often required to deal with a lot of components of different kinds, where it can be an advantage to let each of these become a self-contained object that can largely take care of itself without depending on the main program to know how to do everything the object has to do (encapsulation). This is especially useful in game programming, since every detail in the game can be taken care of by its own encapsulated OOP object quite independently from other objects. This lets each other has its own mind independent from the others, and relieves the main game loop from having to know anything about what the objects are actually doing.
OOP(Object Orinted Programming) 패러다임은 인간이 생각하는 것에 기반을 두었다. 이게 뭔말이냐면 사람들은 이 세상 모든것이 각각의 속성이나 (보이지 않는)잠재성을 가지고 있다고 생각한다는 것이다. 이것은 프로그래밍 세계에서도 동일한데, 프로그램들이 다양한 외부 컴포넌트를 필요로 하는것 보다, 오브젝트가 자신이 필요로 하는 것들을 내부적으로 포함하고 있다면(캡슐화) 훨씬 이득이 생긴다는 것이다. (불필요한 인터페이스를 노출하지 않는것과 외부 의존성이 상당히 낮아지는것을 의미한다.이것은 즉, 프로그래밍 복잡도를 화아악! 끌어 내릴수 있다는 이야기다.) 이건 말이지 특별히 게임 프로그래밍에서 꽤 쓸만한 것이란 말이지. 게임에서 각 오브젝트들은 자신이 할 역할에 충실하게 되고 게임의 루프를 줄여줄수도 있거등.(으윽, 발 의역..T^T)
So, what we are going to do is to write an OOP backbone that we can base our game objects on, and then use it to write an action game. We will need to make an object base class, and find some way of managing all the independent objects so that they can be used. The latter is where STL comes in. But first, we need to know to make a basic object class.
그래서, 우리가 할일은 OOP를 사용해서 우리 게임의 뼈대를 구성하고, 그것을 이용해서 액션게임을 만들거라는 거다.우리는 기반(base) 클래스를 만들어야 하고, 모든 독립적인 객체를 관리할수 있는 방법을 찾아야 한다. 그래야 우리가 걔네를 사용할수 있으니까 :) 나중에 STL을 쓰긴 할테니 걱정은 접으시고.. 우선 기반(base) 클래스 만드는법이나 알아보자고.
4.2 C++ Classes
The C++ programming language has much to offer when it comes to OOP programming. (Of course, there might be better languages when it comes to OOP, but only C++ is (mostly) compatible with the widely-used C, in which Allegro was written.) We will start with a basic class definition for our helicopter object.
C++은 OOP 프로그래밍 하기에 필요한것들을 넘치도록 제공해 준다. (아, 뭐 물론 OOP 에 훨씬 적절한 언어들도 쌔고 쌨지만, C++만이 현재 가장 대중적으로 쓰이-였었지-는 C와 호환을 제공하니까;; 게다가 알레그로는 C로 작성되었다능) 우리는 우리의 헬리콥터 클래스를 만드는 것으로 부터 공부를 시작하겠다.
class chopper {
public:
int X,Y;
};
In many ways, this is a lot like a struct, and can be used in much the same way:
아무리봐도, 이건 구조체랑 무진장 흡사하다. 보통 아래처럼 쓰인다.
#include <allegro.h>
#include "tutorial.h"
#define MIN_Y 8
DATAFILE*data;
BITMAP*backdrop,*framebuf;
class chopper {
public:
int X,Y;
};
int main()
{
allegro_init();
install_keyboard();
initialise_joystick();
data=load_datafile("tutorial.dat");
set_gfx_mode(GFX_VGA,320,200,0,0);
set_palette((RGB*)data[TUT_GAMEPAL].dat);
// create 320x192 backdrop
backdrop=create_bitmap(320,192);
for (int Y=0; Y<128; Y++) hline(backdrop,0,Y,319, (Y/2)+128);
for (int Y=128; Y<192; Y++) hline(backdrop,0,Y,319, ((Y-128)/2)+192);
// create 320x200 double buffer
framebuf=create_bitmap(320,200);
clear(framebuf);
chopper Hero;
Hero.X=0; Hero.Y=100;
while (!key[KEY_ESC]) {
// build frame
blit(backdrop,framebuf,0,0,0,MIN_Y,320,200);
draw_rle_sprite(framebuf,(RLE_SPRITE*)data[TUT_CHOPPER].dat,Hero.X,Hero.Y);
// display frame
vsync();
blit(framebuf,screen,0,0,0,0,320,200);
// get user input
poll_joystick();
if (key[KEY_LEFT]||joy_left) Hero.X--;
if (key[KEY_RIGHT]||joy_right) Hero.X++;
if (key[KEY_UP]||joy_up) Hero.Y--;
if (key[KEY_DOWN]||joy_down) Hero.Y++;
}
return 0;
}
but there is in fact a difference, as you would see if you tried to do
자자, 내가 앞에서 무진장 비슷하다고 했잖아? 근데, 이제 다른걸 알려줄께.
chopper Hero={0,100};
The Hero object is an instance of the chopper class, and not just a plain structure, thus it would not work.
Hero 객체는 chopper 클래스의 인스턴스야, 한마디로 걍 구조체랑은 틀린거란 말이지(구조체 <-> 클래스 이름부터 틀리잖냐). 그래서 뭔말이 하고 싶은거냐면, 위와 같이 초기화 하면 컴파일 안된다- _-
What the public clause means is that whatever is defined in the class (or struct) after it, will be accessible to code outside the class definition as well, which is necessary for parts of the class that isn't self-contained. Since this class isn't self-contained at all yet, both fields (X and Y) have to be marked public.
그리고, public 이라는 녀석은 클래스(구조체에서도 public 쓸수 있음여)가 선언된 다음에 클래스 외부에서 엑세스 가능함을 의미한다. 한마디로 캡슐화가 안되었다는 말이지염. 웅.., 설마 public을 빼먹은 사람이 있다면 당장 x, y 앞에 public이라고 적어주도록 하자굿.
As a first step towards making the object self-contained, we will now move the drawing and input code into it.
한발자국 더 나가서, 드로잉이랑 입력 코드를 클래스 안에다가 박아보자.
#include <allegro.h>
#include "tutorial.h"
#define MIN_Y 8
DATAFILE*data;
BITMAP*backdrop,*framebuf;
class chopper {
public:
int X,Y;
void draw(BITMAP*dest) {
draw_rle_sprite(dest,(RLE_SPRITE*)data[TUT_CHOPPER].dat,X,Y);
}
void input();
};
void chopper::input()
{
if (key[KEY_LEFT]||joy_left) X--;
if (key[KEY_RIGHT]||joy_right) X++;
if (key[KEY_UP]||joy_up) Y--;
if (key[KEY_DOWN]||joy_down) Y++;
}
int main()
{
allegro_init();
install_keyboard();
initialise_joystick();
data=load_datafile("tutorial.dat");
set_gfx_mode(GFX_VGA,320,200,0,0);
set_palette((RGB*)data[TUT_GAMEPAL].dat);
// create 320x192 backdrop
backdrop=create_bitmap(320,192);
for (int Y=0; Y<128; Y++) hline(backdrop,0,Y,319, (Y/2)+128);
for (int Y=128; Y<192; Y++) hline(backdrop,0,Y,319, ((Y-128)/2)+192);
// create 320x200 double buffer
framebuf=create_bitmap(320,200);
clear(framebuf);
chopper Hero;
Hero.X=0; Hero.Y=100;
while (!key[KEY_ESC]) {
// build frame
blit(backdrop,framebuf,0,0,0,MIN_Y,320,200);
Hero.draw(framebuf);
// display frame
vsync();
blit(framebuf,screen,0,0,0,0,320,200);
// get user input
poll_joystick();
Hero.input();
}
return 0;
}
This demonstrates two ways of defining a method. Both chopper::draw() and chopper::input() are methods. Methods are much like functions, except that they operate on an object. chopper::draw() is declared and defined inline, this is often useful to avoid unnecessary method call overhead.
이 예에서는 메쏘드(멤버 함수)를 선언하는 두가지 방법을 보여준다. chopper::draw() 랑 chopper::input() 요녀석이 메쏘드다. 메쏘드는 걍 함수다. 다만, 메쏘드는 동작(operate)의 대상이 오브젝트라는 차이가 있다. chopper::draw() 는 inline으로 선언되었다. 이것은 불필요한 함수 호출 오버헤드를 피할수 있다. (하지만.. 오버헤드가 얼마나 된다고.. 쯥;)
4.3 Constructors
Clearly, we've come a long way towards the encapsulation we want, since the main game loop doesn't have to bother about the coordinates within the object, but still we need to set the initial position. So we should write a method for setting the initial position, but for this we will use a special method, the constructor. The constructor of an object is always called when it is created, and similarly there is a destructor that will be called when it is destroyed. C++ automatically constructs an object when you define a variable of the object type, and destroys it when it goes out of scope, so it's not necessary to do this manually for automatic variables, like our variable Hero.
확실히, 우리는 우리가 바라는대로 캡슐화를 하기 위해서는 갈길이 멀다. 우리가 바라는 캡슐화는 뭐, 게임 루프에서 클래스 내부를 직접 건드릴 필요가 없도록 하는것 정도인데, 그래도 얘가 처음에 나타날 장소 정도는 정해야 할것 같다. 그러니, 우리는 초기화 하는 메쏘드를 만들어야 한다. 근데, 이 초기화를 위해서 우리는 '생성자' 라는 특별한 메쏘드를 작성할 것이다. '생성자'는 객체가 생성될때 불리는 함수이고, 이와 비슷하게 객체가 삭제될때는 '소멸자' 라는 메쏘드가 불린다. C+에서는 객체 타입의 변수(클래스 변수)가 생성될때와 그 변수의 유효 범위를 벗어날때 생성자와 소멸자가 자동으로 불려지게 된다. 따라서 automatic 변수(우리의 Hero와 같이 일반적인 클래스 변수) 에서 우리가 직접 생성자나 소멸자를 호출하는 캐 귀찮은 짓을 하지 않아도 된다는 말이다.
We will define two constructors, the default constructor that sets a default initial position, and a constructor where it is possible to specify the initial position explicitly. C++ allows several functions, methods, and operators with the same name but with different parameter lists to coexist, a feature called "overloading", where the compiler automatically chooses the right function according to the given parameters. We will use this feature to have two different constructors. Change the class definition to read:
우리는 생성자를 두개를 만들거다. 우선 첫번째는 고정된 초기화 값으로 초기화 하는 생성자를 만들것이고 두번째 생성자는 원하는 초기화 값을 설정할수 있도록 할것이다. C++은 동일한 이름에 변수값만 다른 함수들, 메쏘드들, 동작들을 할수 있도록 해 놓았는데, 이를 '오버로딩'이라고 한다. 얘(오버로딩 기능)는 이름과 매개변수들로 원하는 함수를 정확히 찾아서 호출한다. 우리는 이 기능(...)을 이용해서 두개의 다른 생성자(생성자 이름은 동일하겠지...)를 만들것이다. 다음을 보자.
class chopper {
int X,Y;
public:
chopper() { X=Y=0; }
chopper(int _X,int _Y) { X=_X; Y=_Y; }
void draw(BITMAP*dest) {
draw_rle_sprite(dest,(RLE_SPRITE*)data[TUT_CHOPPER].dat,X,Y);
}
void input();
};
Then remove the line
그리고 다음 줄을 지우자.
Hero.X=0; Hero.Y=100;
and try it. The object is now completely encapsulated, the main loop doesn't know anything about X or Y. We have therefore taken them away from under the public clause; they are thus now private to the object, and hidden from everything else. Note that the helicopter now starts at the top left of the screen, because we put the default position in the default constructor to be 0,0. To try the alternate constructor, replace
그리고 시도(try) 해보자. (뭘0,.0?). 이제 그 오브젝트(Hero)는 메인루프에서 X,Y 라는것에 대해 전혀 모를만큼 완전히 캡슐화 되었다. 우리는 걔네(X,Y)를 private로 옮겨버렸기 때문에, 다른 모든것으로부터 감춰진 것이다. 참고, 헬리콥터는 좌 상단에서부터 출력되는데, 우리가 기본 생성자에서의 좌표를 0,0으로 설정했기 때문이다. 만약 초기화 좌표를 바꾸고 싶다면 다음과 같이 수정하면 된다.
chopper Hero;
with
chopper Hero(50,100);
This will still define the variable Hero to be of the data type chopper, but since chopper is a class, its constructor must be called when it is created. This means that we will call the constructor that takes two integer values and use it, giving the values 50 and 100, instead of using the default constructor. This alternate constructor happens to set the initial position to the given values.
Hero 변수는 여전히 chopper 타입의 변수일 뿐이지만, chopper는 클래스 이기 때문에 생성자는 변수가 생성될때 호출된다. (그래서 chopper Hero 시점에 (50, 100) 처럼 -생성자에-인자를 넘기는 거지.) 이 말인 즉슨, 위 내용을 보면, 클래스 변수인 Hero가 초기화 될때 기본 생성자를 호출하도록 되어 있었는데, 우리가 50,100 을 매개변수로 받는 생성자를 호출하도록 변경했다는 말이 되겠습니다. 이 alternate(대안...;) 생성자는 매개변수로 받은 값으로 초기 위치를 지정하도록 되어있다.
4.4 Inheritance
One of the most important advantages OOP has over traditional procedural programming, is the concept of inheritance. This means that it is possible to write a base class with all the basic and abstract functionality, variables, and common interface you need, and then write derived classes that inherits everything the base class has, and adds its own functionality. Not only can this reduce the amount of coding by letting any derived class use the base class's functionality when it needs to, thus simplifying implementation of similar classes and structures, but there is also another very important implication: the derived class is compatible with the base class, and can be used in place of the base class. This allows a system to override any of the base class functionality it wants, and use the new objects where the base class would be used, thus adapting it to its own needs. For us, this is very important; we can make an array of pointers to base class objects, and go through this array at any time, calling the methods of its elements. Depending on which derived class is present in this array, the individual objects can respond in completely different ways to each other, without the calling code having to know anything about it.
전통적인 프로그래밍 언어들(C라던가 아니면 C라던가..;)에 비해 OOP 의 중요한 이점중에 하나는 상속이라는 컨셉이다. 상속이라는게 뭐냐면 말이야. 베이스 클래스에서 기본적이고 추상적인 함수라던지 변수라던지 인터페이스라던지를 만들어 놓고, 자식 클래스에서 베이스 클래스의 내용을 다 가져오면서도 추가적으로 자신이 필요로 하는 기능을 더할수 있다는 것이지. 이거는 말이야 코딩량을 줄여준다는 잇점뿐 아니라, 베이스 클래스의 기능이 필요할때 쓸수있고, 그로인해 비슷비슷한 클래스나 구조체를 구현하기 간단해 진다는 장점이 있다는 말이지영. 또 중요한거 한개: 자식(derived) 클래스는 베이스 클래스랑 호환되기 때문에 베이스 클래스가 쓰여야 할곳에서 자식 클래스가 대신 쓰일수도 있다는 것이다. 한마디로, 베이스 클래스에다가 필요로 하는 기능을 덧 붙일수도 있는거고(자식 클래스), 필요하다면 베이스 클래스가 쓰여야 할곳에 자식클래스를 대신 사용할수도 있다는 이야기다. 요거요거요거는 우리한테 무진장 중요한거다: 우리는 베이스 클래스 객체의 포인터 배열을 만들수 있고, 언제라도 이 배열을 통해서 필요한 메쏘드를 부를수 있다. 이 (포인터)배열에서 자식 클래스에 따라 각기 다른 응답을 하도록 할 수 있다. (음.. 발번역이라 내용이 많이 헷갈리는데, 이거 업캐스팅이랑 다운캐스팅 이야기 하는거 아닐려나...- .-;)
So, what we are going to do is to write a base class that we can use as a foundation for our game elements. We will also use the Allegro-defined fix data type here, to enable sprites to move a fractional amount of pixels per frame. This will enable objects to move slower than one pixel per frame without standing totally still. Also, we won't bother to have a default constructor.
따라서, 이제 우리가 해야할일은 우리의 게임에서 쓰일 element의 베이스 클래스를 작성하는 것이다. 우리는 각 프레임에서 스프라이트 처리를 위해서 Allegro에서 (미리)선언되어 있는 fix 데이터 타입을 사용할 것이다. 이것은 한 프레임당 픽셀 하나 찍는것 보다는 느리게 동작할 것이다.(...이거 맞는 번역임?) 그리고, 우리는 기본 생성자 코드를
class sprite {
protected:
fix X,Y;
public:
sprite(fix _X,fix _Y) { X=_X; Y=_Y; }
virtual ~sprite() {}
virtual void draw(BITMAP*dest) {}
virtual void animate() {}
};
The protected clause is similar to private, except that it allows derived classes to access the elements defined protected. We have now also declared the methods virtual, which means that they are encoded into the object structure in such a way that they can be overridden transparently by derived classes, even if the code that uses the object thinks it's an instance the base class.
protected 는 private와 비슷하다, 다만, 자식 클래스에서 액세스 가능하다는 차이가 있다. virtual 이라는 키워드를 포함하는 함수도 볼수가 있는데, 이것은 자식클래스에서 투명하게 override 가능하도록 한다. 다시 말해 자식 클래스를 참조(& or *)하는 베이스 클래스의 인스턴스가 virtual 함수를 호출하면 자식 클래스에서 선언된(오버라이드) 함수를 콜함. (http://www.hybrid.pe.kr/tt/305 참고)
When we use this base class, this is how our program will look like.
이 베이스 클래스를 사용하면, 우리의 프로그램이 다음과 같이 바뀐다능.
#include <allegro.h>
#include "tutorial.h"
#define MIN_Y 8
DATAFILE*data;
BITMAP*backdrop,*framebuf;
class sprite {
protected:
fix X,Y;
public:
sprite(fix _X,fix _Y) { X=_X; Y=_Y; }
virtual ~sprite() {}
virtual void draw(BITMAP*dest) {}
virtual void animate() {}
};
class chopper : public sprite {
public:
chopper(fix _X,fix _Y) : sprite(_X,_Y) {}
virtual void draw(BITMAP*dest) {
draw_rle_sprite(dest,(RLE_SPRITE*)data[TUT_CHOPPER].dat,X,Y);
}
virtual void animate();
};
void chopper::animate()
{
// seems postfix operators aren't defined for the fix class
if (key[KEY_LEFT]||joy_left) --X;
if (key[KEY_RIGHT]||joy_right) ++X;
if (key[KEY_UP]||joy_up) --Y;
if (key[KEY_DOWN]||joy_down) ++Y;
}
int main()
{
allegro_init();
install_keyboard();
initialise_joystick();
data=load_datafile("tutorial.dat");
set_gfx_mode(GFX_VGA,320,200,0,0);
set_palette((RGB*)data[TUT_GAMEPAL].dat);
// create 320x192 backdrop
backdrop=create_bitmap(320,192);
for (int Y=0; Y<128; Y++) hline(backdrop,0,Y,319, (Y/2)+128);
for (int Y=128; Y<192; Y++) hline(backdrop,0,Y,319, ((Y-128)/2)+192);
// create 320x200 double buffer
framebuf=create_bitmap(320,200);
clear(framebuf);
chopper Hero(50,100);
while (!key[KEY_ESC]) {
// build frame
blit(backdrop,framebuf,0,0,0,MIN_Y,320,200);
Hero.draw(framebuf);
// display frame
vsync();
blit(framebuf,screen,0,0,0,0,320,200);
// get user input
poll_joystick();
Hero.animate();
}
return 0;
}
As you can see, you use colons to inherit a base class, as well as to use an inherited constructor. The base class is inherited public to make public members of the base class remain public, and not turned private.
보다시피, 베이스 클래스에서 상속받기 위해서는 : 을 쓰면 된다. 베이스 클래스에서 public이었던 것들은 상속받은 후에도 public으로 남아 있으니, 상속 받으면 private로 변한다거나.. 하면서 두려움에 떨지 않아도 된다.-.-;
To learn more, proceed to the next chapter
음.. 내가 번역을 하고 있긴 하지만, 나같아도 원어로 보겠다 싶은 수준의 발번역..;; 어쨌든 다음 챕터로 궈궈궛-0-
refer: http://www.ping.uio.no/~ovehk/allegro/tut4.html
PS. 아우~ 번역하기 귀찮아. 내일 회사에서 심심할때 번역해야겠당.
이글루스 가든 - 하루에 한 장~ 꾸준히 번역하기
이글루스 가든 - 하루에 한 장~ 꾸준히 번역하기


덧글
나쯔 2009/09/15 08:49 # 답글
니가 고생이 많다?
승네군 2009/09/15 14:55 # 삭제 답글
뭐.. 업무시간 땡땡...;