페이지

2012년 6월 1일 금요일

[C++ Study - 5.12] 중급Part - 클래스 재활용 - 중첩클래스


이 블로그의 문서는 winapi.co.kr을 참조하여 제가 공부하고 이해한 내용을 바탕으로 정리한 문서 입니다. 그러므로 학습 순서는 winapi.co.kr의 학습 순서와 동일합니다.
이 문서는 제가 공부한 내용을 정리하는데 목적이 있습니다.
winapi.co.kr을 보고  한것이라 사이트 내용과 100% 일치되는 문단이 있을 수 있으며, 문제가 될시 삭제하도록 하겠습니다.


1. 중첩클래스

중첩 클래스란 클래스 선언문안에 다른 클래스가 선언되는 형태이다. 특정 클래스를 구현하기 위한 보조 클래스가 필요한데 보조 클래스는 오직 이 클래스 내부에서만 사용하며 외부에는 전혀 알릴 필요가 없다면 이때 클래스를 중첩시킨다. 클래스에 캡슐화되는 것은 흔히 멤버 변수, 멤버 함수 정도이지만 타입도 포함될 수 있다. 클래스가 타입이므로 다른 클래스에 포함될 수 있는 것은 당연하며 열거형이나 typedef로 정의한 타입도 물론 가능하다. 다음 예제를 보자.
class Outer
{
private:
     class Inner
     {
     private:
          int memA;
     public:
          Inner(int a) : memA(a) { }
          int GetA() { return memA; }
     } obj;
public:
     Outer(int a) : obj(a) { }
     void OutOuter() { printf("멤버값 = %d\n",obj.GetA()); }
};

void main()
{
     Outer O(345);
//  Inner I(678);       // 에러

     O.OutOuter();
}
Outer 클래스 선언문안에 Inner 클래스 선언문이 있고 Inner 클래스형의 객체 obj를 멤버로 포함하고 있다. Inner 클래스는 Outer 클래스 내부에서만 사용하므로 외부로는 전혀 알려지지 않는다. 그래서 main에서는 Inner 타입의 객체를 선언할 수 없다. 즉 Inner는 Outer 클래스에 대해 지역적인 클래스라고 할 수 있다. 만약 Inner를 외부에도 알리고 싶다면 public: 영역에서 선언하면 된다. 이 경우 외부에서는 Outer::Inner I(1234); 식으로 이 타입이 소속되어 있는 클래스를 지정해야 한다.
클래스가 하는 일이 굉장히 복잡해서 도우미 클래스를 만들어야 할 필요가 있다거나 내부적으로만 사용해야하는 타입이 있다면 이런 식으로 클래스 선언을 중첩시킨다. 다음 클래스는 이중 연결 리스트를 표현하는데 연결 리스트는 저장되는 데이터와 양방향의 링크로 구성된 노드의 집합이다. 노드들은 클래스 내부에서 완벽하게 관리할 수 있으므로 외부에서 굳이 알 필요가 없으므로 LinkedList 클래스 안에 선언했다.

class LinkedList
{
private:
     struct Node {
          int data;
          Node *prev,*next;
     };
     Node *head,*tail;

public:
     LinkedList();
     ~LinkedList();
     Insert(Node *p,int a);
     Delete(Node *p);
     int GetData(Node *p);
};
외부에서는 Insert, Delete 등의 공개된 인터페이스 함수로 정수값을 넣거나 빼기만 할 뿐이고 모든 내부적인 자료 관리는 클래스에 완벽하게 캡슐화되어 있다. 물론 외부에서도 Node를 꼭 알아야 할 필요가 있다면 이 클래스를 퍼블릭 영역에 선언할 수도 있다. Node는 LinkedList에서만 필요하며 외부에 따로 정의해 봐야 아무짝에도 쓸모가 없으므로 지역 타입이 되는 것이 옳다. LinkedList가 Node를 완전히 포함하고 있으면 이 클래스는 자체적으로 동작할 수 있는 모든 것을 가지므로 재사용성과 범용성이 증가한다.
클래스 선언 자체가 지역적이라는 것과 클래스가 멤버 함수를 포함할 수 있다는 점을 활용하면 C/C++ 언어가 지원하지 않는 지역 함수를 만드는 것도 가능하다. 원래 C의 함수는 상호 평등한 관계에 있어 함수끼리 종속되지 않는다. 그러나 클래스가 개입되면 함수끼리 종속시킬 수도 있고 파스칼처럼 지역 함수를 사용할 수도 있다. C++이 언어 차원에서 지역 함수를 지원하는 것은 아니지만 지역 타입을 허용하며 클래스라는 타입이 함수를 가질 수 있으므로 지역 함수가 가능해진 것이다. 다음 예제를 보자.

void func()
{
     struct dummy {
          static void localfunc() {
              puts("저는 func 안에서만 정의됩니다.");
          }
     };

     dummy::localfunc();
}

void main()
{
     func();
     // localfunc();        // 에러
}
func 함수 내부에서 dummy라는 이름으로 지역 클래스를 선언하고 이 클래스의 정적 함수 localfunc를 인라인으로 정의하고 있다. dummy를 struct로 선언했는데 class로 선언하고 public:을 붙이는 것과 같다. 정적으로 선언했으므로 호출할 때는 반드시 클래스 이름과 함께 사용해야 한다. 지역 클래스의 함수는 static이어야 하는데 그래야 객체없이 클래스명으로 바로 호출 가능하다. 만약 정적이 아니라면 이 함수를 호출하기 위해 dummy형 객체를 하나 선언해야 하므로 불편해진다.
localfunc는 func 함수 내부에서 정의한 지역 클래스 소속의 정적 멤버 함수이므로 이 함수는 func 외부로는 알려지지 않는다. 따라서 main 함수에서는 이 함수를 호출할 수 없다. 사실 main에게는 localfunc 뿐만 아니라 dummy라는 지역 클래스 자체가 알려지지 않는다.
이 예제는 C/C++로도 지역 함수를 만들 수 있다는 것을 보여줄 뿐 실용적 가치는 별로 없다. 게다가 이렇게 만든 함수는 인라인만 가능하다는 점에서 복잡한 동작을 처리하기에는 한계가 있다. 아주 긴 함수가 쓰는 작은 유틸리티 함수 집합을 이런 식으로 정의해 놓으면 배포하기 편리하다는 정도의 활용 방안이 있지만 문법을 변칙적으로 쓰면 이런 것도 가능하다는 흥미거리 정도밖에 안되는 것 같다.

댓글 없음:

댓글 쓰기