[완료] C++ 중 추상 기반 클래스에 대하여...(abstract base classes)
또 올리게 되네요;;
C++ 예제 중에 다형성에 관한 이 예제로 인해 상당히 머리가 아프네요...
소스부터 올릴께요..
#include
using namespace std;
class CPolygon
{
protected :
int width, height;
public :
CPolygon () : width ( 2 ), height ( 2 )
{
}
void set_values ( int a, int b )
{
width = a;
height = b;
}
virtual int area ( )
{
return 0;
}
void printarea ( )
{
cout << this->area ( ) << endl;
}
};
class CRectangle : public CPolygon
{
public :
/*
int area ( )
{
return ( width * height );
}
*/
};
class CTriangle : public CPolygon
{
public :
int area ( )
{
return ( width * height / 2 );
}
};
int main ( int argc, char *argv )
{
CRectangle rect;
CTriangle trgl;
CPolygon ply;
CPolygon *ppoly1, *ppoly2;
ppoly1 = ▭
ppoly2 = &trgl;
/*
rect.set_values ( 4, 5 );
trgl.set_values ( 4, 5 );
*/
cout << sizeof ( ppoly1 ) << " is sizeof ( ppoly1 )" << endl;
cout << sizeof ( ppoly2 ) << " is sizeof ( ppoly2 )" << endl;
ppoly1 -> set_values ( 4, 5 );
ppoly2 -> set_values ( 4, 5 );
ppoly1 -> printarea ( );
ppoly2 -> printarea ( );
ply.printarea ( );
return 0;
}
파일을 보시면 아시겠지만...
===================== 이하는 ======================
우리는 함수에 대한 명령 행을 지정하는 것 대신에 virtual int area (void)에 =0을 추가했다는 사실에 주목하십시오. 이러한 함수의 형은 순수 가상 함수(pure virtual function)이라는 이름으로 받아들여지게 되며, 순수 가상 함수(pure virtual function)를 포함하고 있는 모든 클래스는 추상 기반 클래스(abstract base classes)로 간주합니다.
추상 기반 클래스(abstract base classes)의 가장 큰 차이점은 이것들은 그것의 객체(object)를 만들지 않는다는 것입니다. 그러나 우리는 그것에 대한 포인터는 만들 수 있습니다. 그러므로 다음과 같은 선언을 하는 것은 위에서 선언된 추상 기반 클래스에는 적당하지 않습니다.
CPolygon poly;
그럼에도 불구하고 다음과 같은 포인터들은 올바로 동작합니다.
CPolygon * ppoly1;
CPolygon * ppoly2
이것은 순수 가상 함수(pure virtual function)가 정의 부분을 포함하고 있지 않고, 그것의 멤버가 정의되지 않은 모든 것은 개체를 생성하는 것이 불가능하기 때문입니다. 그럼에도 불구하고 함수가 정의되지 않은 채 파생된 클래스의 개체를 가리키는 포인터는 완전히 타당한 것입니다.
================= 책 내용 입니다 ===================
볼드체 표시한 부분에서 헷갈리는 것이...
1. 추상 기반 클래스는 객체를 만들지 않는다고 하였습니다. 하지만 객체가 없이 개체가 만들어 지나요..-_-; 아무튼
예제에 보시는 바와 같이 (조금 수정하였지만..) 생성이 됩니다... 또한 생성된 개체를 사용하여 포인터를 사용한 예제와 같은 결과도 출력하였습니다..
가정해서 생각해 보건데 warnning 없이 컴파일 되었으면 제대로 만들어진 것이 아닐까..하는 가정하에 생각해봤습니다만..
개체를 제대로 생성하였고, 사용하는데 오류없이 정상동작 하였다. 그리고 포인터와 비교하여 볼 때에 같은 결과를
(생성자와 소멸자 함수 만들어서 일일이 찍어 보았습니다....)
만들어낸다면, 뭐하러 포인터를 사용하나...? 오히려 포인터 잡는데에 4byte나 더 먹게 되는데 말이죠...
2. 또한 추상화 기반 클래스라는 것이 왜 '추상화 기반 클래스'라는 이름까지 붙어가며 특별해야 하는지... 도무지 이해가 가지 않습니다...
아는 지식이 일말이라도 있다면 도와주십쇼 ㅠ.ㅠ 정말 머리털 하나 하나가 아까운 때에 팍팍 빠져갑니다..ㅠ.ㅠ
수정하신 부분이
수정하신 부분이 아마 area함수인듯한데요, 이게 결정적인 차이입니다.
bncrux께서 area함수를 순수가상함수에서 가상함수로 수정했기 때문에 더이상 CPolygon은 추상클래스가 아니게 된것이고 그러므로 이것의 인스턴스를 만들수 있게 된 것입니다.
책에 적혀있는대로 virtual int area() = 0 라고 하고 컴파일해보시면 워닝이 아니라 에러가 나올것입니다.
아...책 마지막 부분에 있었네요 =0 ㅠㅠ
감사합니다.. pure virtual function... 절대 잊지 못하겠네요..
답변 정말 감사합니다.
아...그렇군요...그러나 질문 하나 더 ㅎ;;
순수가상함수로 선언한다는 것과 가상함수로 선언한다는 것이 virtual int area()에 대입연산자로 값을 넣느냐 아니면 함수를 정의 하느냐에 따라
다른것인가보군요....
맞나요 ㅠㅠ?
순수가상함수와 가상함수의 차이점에 대해서 알려주신다면 정말 ㅠㅠ...
함수의 정의가
함수의 정의가 있느냐 없느냐로는 순수가상함수와 비순수가상함수를 구분 할 수 없습니다.
왜냐면 (그것이 올바른 프로그래밍 습관인지 아닌지는 둘째치고) 순수가상함수라고 해도 함수정의는 가질수 있거든요.
선언부에 int area() = 0; 이라고 하고 int CPolygon::area() {...} 처럼 정의하는 것도 가능합니다.
일단 순수가상함수로 선언하는 방법은 =0을 쓰면 되는 것이긴한데, 순수가상함수와 비순수가상함수는 정의가 있다 없다보다는 의미적인 면으로 구분해야 한다고 생각합니다.
가상함수는 상속받는 클래스에서 이 함수의 동작을 바꿀 수 있는 함수입니다.
순수가상함수는 이것을 상속받는 클래스에서 '구현해야할 함수'입니다.
실제로 어떤걸 구현할때 이것을 가상함수로 할지 순수가상함수로할지는 순전히 프로그래머의 몫입니다.
예를 들어, AbstractCar 라는 자동차의 추상클래스를 만든다고 합니다.
이때, 주행하는 함수로 drive라는 함수를 만든다고 하면, 이 함수는 순수가상함수이어야 할 가능성이 높습니다(제가 자동차에 대한 지식이 없어서 확신은 못하지만요).
왜냐하면 엔진이 가솔린엔진이냐 디젤엔진이냐에 따라서 구현이 달라질테니 여기서 drive를 정의하는건 어렵고, 그렇다고 자동차로서 drive라는 기능이 없다는 건 말이 안되니까, 순수가상함수로 넣어서 상속받는 클래스에서 정의하도록 하는 것이죠.
한편 (좀 예가 서툴지만) 라디오를 재생해주는 playRadio()라는 함수와 라디오를 들을수 있는지 확인하는 bool radioExists()라는 함수가 있다면, (저라면) radioExists()는 가상함수로 해서 bool AbstractCar::radioExists() {return false;} 와 같이 하고, playRadio()는 보통 함수로 void playRadio() {if (radioExists()) {...}} 이런식으로 구현할 것같습니다.
일단 라디오를 재생하는 것은 어차피 라디오가 해주는 기능이므로 자동차에서 이러쿵 저러쿵 할게 아니고, 라디오가 있으면 라디오를 키면 될일이므로 딱히 오버라이딩될 필요는 없어보입니다.
반면에 라디오가 없는데 라디오를 켠다는건 불가능하고, (사실 모두 가지고 있을 것 같지만-_-;) 모든 자동차가 라디오를 가지고 있는 것은 아니므로, 우선 false를 반환하게 한후, 이를 상속받는 클래스중에 라디오를 가지고 있는 자동차에서 오버라이딩해서 true를 반환하도록하면 될것으로 보입니다.
위처럼 구현하는게 올바르다는게 아니라 하나의 예일 뿐입니다. 애시당초 라디오 기능은 AbstractCar에서는 제외하고 이를 상속받아서 RadioPlayableCar같은 클래스를 만들어서 여기서 구현하도록 설계하는 것도 가능하니까요.
위에도 적었듯이 이런부분은 프로그래머가 결정할 부분입니다.
아..말씀하신대로 됩니다...
감사의 말을 너무 남발하는 것이 아닌가 싶으면서도 그래도 감사합니다. ^_^
말씀하신대로 pure virtual function 으로 인해 base class가 abstract class되어 버려서
선언을 해 주어도 abstract class를 개체화 시킬 수 없고,
상속받은 class에서 overriding해버리면 기존의 것이 무시되기 때문에 그다지 의미가 없다는 사실을 깨달았습니다. ㅎㅎ
pure virtual function를 사용 할 때에는 그냥 =0; 로 끝나던지,
후에 혹시라도 볼 수 있을 경우를 대비하여 내용을 써 주는 것도 좋겠군요... (물론 프로그래머 마음이겠죠? ^^)
아무튼 이해하는데에 엄청난 도움을 주셔서 상당히 감사합니다!!!!! 최고!!!!!!
댓글 달기