프로그래밍/C++2012. 4. 26. 10:49

1. 순수 가상함수와 추상 클래스

 

추상클래스를 만들기 위해서는 가상함수를 선언해야 하며, 0으로 초기화 시켜주어야 한다.

추상클래스의 목적은 이를 상속하는 하위 클래스에게 구현의 임무를 부여하는 데 있다.

 

인터페이스의 탄생

 

좀 더 이쁘게 할려면, 이렇게 바꾸자.

 

인터페이스는 기능별로 나누는게 좋고, 다중상속으로 처리하면 된다.

 

 

 

 

다음은 메뉴만들기를 객체 지향적으로 구현해 본 것이다.

 

메뉴를 만든다면, 우선 여러가지 형태가 있겠지만,

추상메뉴

 |-  팝업 메뉴     Vector<"추상메뉴">

  `-  메뉴 아이템

 

팝업메뉴는 여러가지 메뉴가 있을테니, Vector<"추상메뉴">로 구현하면 되고,

모든 메뉴는 title 및 command가 있으니, 이는 추상메뉴에 구현한다.

 

이렇게 하위 객체를 부모로 싸잡아서 구현하는 걸 composite 패턴이라고 한다. 

 

밑은 composite 패턴으로 구현한 고객관리 메뉴 코드이다.  동작성은 없다.

 

 

자원 반납을 위해서, 소멸자를 추가하자.

 

 

 

가상 소멸자

이렇게 해서 자원을 해지하려면, B가 해지되는게 아니라, A가 해지된다.  이를 해결하기 위해선,

~A() {cout << "메모리A해지" <<endl;}

앞에 virtual을 붙여서

 virtual ~A() {cout << "메모리A해지" <<endl;}
이라고 정의하면 된다. 

 

근데 또 가상소멸자에 의한 가상테이블이 생기는 것을 없애려, protected로 선언해, 컴파일 시 에러가 나게 하는 방법도 있다.

소멸자는 delete static_cast<B*>(p);로 바꿔야 한다.

 

그러나 delete static_ca.. 아 쓰기도 어려운데, 그래서 할 수 있는 방법이 다음과 같다.

.... 걍 c++ 하지 말자.

 

정리하자면, 상속관계에 있는 클래스에서 부모 클래스이 소멸자는 항상 가상함수로 만들자.

 

갑자기 생각난  mix-in 하고 upcasting하고 어떻게 틀리더라...

 

 

가상함수를 쓰면 다음과 같은 결과를 얻는다.

주석에도 있듯이,

foo가 가상이 아니라면 : static binding => p의 포인터타입으로 결정
                                 A::foo()호출
foo가 가상이라면        : *p[1]() 라고 기계어 코드를 생성해 놓는다.

 

*p[1]은 실제로 가상함수테이블(Virtual Function Table)의 인덱스를 가리킨다.  보통 컴파일러는 가상함수의 기능을 구현하기 위해 가상함수 테이블과 가상함수 테이블 포인터를 사용하는데, 가상함수테이블은 가상함수의 모든 주소를 가지고 있는 배열이며, 가상함수 포인터는 이 테이블을 가리킨다.

 

즉 가상함수를 사용하면, 다형성을을 얻을 수 있으나,

 

1. 가상함수 테이블을 위한 메모리 할당,

2. 가상함수 테이블 포인터로 인한 객체의 크기 증가,

3. 함수 호출시 함수 포인터에 의한 호출로 오버헤드 증가,

4. Inline을 사용하지 못함

 

다는 단점도 있다.  그리고 한가지 더! 가상함수는 run-time에 호출 될 함수가 결정되지만, default paramemter는 compile-time에 결정된다.  따라서 예상치 못한 결과가 나올 수 있으니 주의하자.

 


composite 패턴 실습

 

다음은 composite 패턴을 이용해서 만들어 본 비디오 대여점 메뉴이다. 기본적인 메뉴만 구성되어 있다.

 

 

 

 

Observer 패턴 실습

위에서 생성된 메뉴의 메세지를 처리하기 위한 모든 객체는 인터페이스를 정하고, 그 인터페이스로부터 상속받은 형태로 구현 할 수 있다.

관찰자(Observer)패턴은 하나의 상태 변화를 여러 객체에게 알려주는 패턴이다.

 

 

 

객체의 이벤트 처리를 위해 인터페이스 방식(바로 전 예제)이 아닌 함수와 연결 하는 방식도 있다.

다음을 보자.

 

 

 

다중상속

 

다중상속에는 여러가지 문제점 있겠지만, 아래와 같이 (주석처리 된 부분) 다중상속시에 발생할 수 있는 문제점이 있다.

 

주석같이 상속 받으면,

 

                  RefBase

                 /           \         

        IInterface         BpRefBase

                 \            /

                  HelloWord

 

IInterface, BpRefBase 모두 mCount라는 변수를 상속 받았으니, 컴파일러는 어느 녀석을 써야 하는지 모른다.  그래서 대신, virtual 키워드를 써서 처리해야 한다.  아니 이런 구조를 아예 피하자.

 

 

 

다중상속과 캐스팅

 

reinterpret_cast<B*>(&c)는 c를 어떻게든 B처럼 보이게하라고 캐스팅 한다. 즉 C안에서 B의 위치를 찾는게 아니라, C가 B처럼 되는 것이다. 그래서 만약 우리가

 

한편 x와 y가 public으로 바뀌서, y에 값(10)을 대입해 보면 y가 아닌 x가 10으로 대입되는 걸 볼 수있다. (x가 먼저 상속받기 때문에, 그 주소에는 x가 먼저 위치하고, 그 다음에 y가 위치한다.)

 

 

 

 

RTTI (Run-Time Type Informationi)

런타임에 typeinfo를 가져온다.  typeinfo는 가상함수테이블 상위에 위치하고 있으며, 가상함수가 있을 때만 존재한다.

 

 

 

 

 

 

 

 

 

템플릿

 

템플릿과 타입

 

 


 

'프로그래밍 > C++' 카테고리의 다른 글

생성자 뒤에 : 붙는 경우  (0) 2013.01.01
cout , endl 의 원리  (0) 2012.04.28
[C++] Smart Pointer  (0) 2012.04.25
Android Framework 분석을 위한 C++ 3일차  (0) 2012.04.25
swap 함수  (0) 2012.04.25
Posted by code cat