이번에는 객체지향언어의 다형성을 이용하여 하나의 컨테이너로 여러가지 클래스를 다루는 방법과
포인터를 요소로 갖는 컨테이너의 삭제하는 방법을 보겠습니다.
일반적으로 그래픽 에디터 같은 프로그램에서 여러 도형 객체를 클라이언트에서 하나의 자료 구조로 다룬다고 할 때
베이스 클래스로 인터페이스 표준을 따르게 하고 각 도형 클래스는 베이스클래스를 상속( Abstarct Inheritance )하여
다형성(Polymorphism)을 이용합니다.
우선 아래 코드의 전체 내용을 먼저 보시기 바랍니다.
1 #include <iostream> 2 #include <vector> 3 4 using namespace std; 5 6 class CShape { 7 public: 8 virtual void draw() = 0; 9 }; 10 11 class CCircle : public CShape { 12 public: 13 void draw() { cout << "Circle" << endl; } 14 }; 15 16 class CSquare : public CShape { 17 public: 18 void draw() { cout << "Square" << endl; } 19 }; 20 21 struct SDeleteObject{ 22 template< typename T > 23 void operator()(const T* ptr) const 24 { 25 delete ptr; 26 } 27 }; 28 29 30 int main() 31 { 32 vector<CShape*> vecShape; 33 34 vecShape.push_back( new CCircle ); 35 vecShape.push_back( new CSquare ); 36 vecShape.push_back( new CCircle ); 37 38 for ( size_t i = 0 ; i < vecShape.size() ; ++i ) { 39 vecShape[i]->draw(); 40 } 41 42 for_each( vecShape.begin(), vecShape.end(), SDeleteObject() ); 43 44 return EXIT_SUCCESS; 45 } |
---------- run - C/C++ ----------
Circle
Square
Circle
출력 완료 (0초 경과)
CCircle, CSquare는 CShape를 Base로 하고 있습니다.
CShape는 인터페이스 표준화를 위해 draw() 를 순수 가상 함수로 선언 하였습니다.
main() 부분은
32번 라인에서 vector<CShape*> 형으로 vecShape를 만들고
34~36번 라인에서 원과 사각형을 추가 합니다.
38~40번 라인에서 각 요소의 draw()를 실행하고,
42번 라인에서 for_each에 predicator로 SDeleteObject를 이용하여 컨테이너 소멸 전에 요소를 삭제합니다.
SDeleteObject는 이전에도 여러 번 등장했던 Function object 입니다.
Scott 아저씨의 Effective STL 서적을 보시면 보다 많은 내용을 보실 수 있습니다.
template의 단점입니다만, STL의 컨테이너의 단점 중 하나가 타입별로 컨테이너 클래스가 생성되기 때문에
자원의 낭비가 많다는 점입니다.
말그대로
vector<int> vi;
vector<double> vd;
vector<CUserClass> vc;
이런식으로 선언하고 컴파일 하면 vector class가 하나만 있는게 아니라 vector와 동일한 구조의 class가
각 타입별로 따로 따로 생성되고 Runtime시 부담이 된다는 것이죠.
이를 개선 할 수 있는 방법이 컨테이너의 요소로 void* 를 이용하는 것입니다.
vector<void*> 이렇게 하면 타입 케시팅하는 불편함은 있지만,
사용자 환경의 자원이 부족한 상황에서는 선택할 수 밖에 없는 방법이기도 합니다.
이번에는 조금만 범위를 넓혀서 포인터를 갖는 컨테이너를 만들고 Function Object를 이용해서 Sort 하는 것을 만들어 봅시다.
1 #include <iostream> 2 #include <vector> 3 #include <string> 4 #include <algorithm> 5 #include <functional> 6 7 using namespace std; 8 9 #define PI 3.141592653589793238462643383279502884197 10 11 class CShape { 12 public: 13 virtual ~CShape(){} 14 virtual void draw() = 0; 15 virtual double getArea() const = 0; 16 virtual string getShapeType() const { return strShapeType_; } 17 protected: 18 CShape( const string& st = "" ) : strShapeType_(st) {} 19 private: 20 string strShapeType_; 21 }; 22 23 class CCircle : public CShape { 24 public: 25 CCircle( double r ) : CShape("Circle"), radius_(r) {} 26 virtual ~CCircle(){} 27 virtual void draw() { cout << getShapeType() << endl; } 28 virtual double getArea() const { return 2. * PI * radius_; } 29 private: 30 double radius_; 31 }; 32 33 class CRectangle : public CShape { 34 public: 35 CRectangle( double w, double l ) : CShape("Rectangle"), width_(w), length_(l) {} 36 virtual ~CRectangle(){} 37 virtual void draw() { cout << getShapeType() << endl; } 38 virtual double getArea() const { return width_ * length_; } 39 private: 40 double width_; 41 double length_; 42 }; 43 44 45 // SDeleteObject is function object for to delete object in container 46 struct SDeleteObject{ 47 template< typename T > 48 void operator()(const T* ptr) const 49 { 50 delete ptr; 51 } 52 }; 53 54 // SFOCompareShapeArea is function object for area of shape comparison 55 struct SFOCompareShapeArea : public binary_function< CShape*, CShape*, bool > { 56 bool operator()( const CShape* s1, const CShape* s2 ) const 57 { 58 return s1->getArea() < s2->getArea(); 59 } 60 }; 61 62 63 typedef vector<CShape*> Vec_PSHAPE; 64 65 void fnPrint( const Vec_PSHAPE& vps, const string strComment = "" ) 66 { 67 cout << strComment << endl; 68 for ( size_t i = 0 ; i < vps.size() ; ++i ) { 69 cout << "Shape type : " << vps[i]->getShapeType() 70 << "\tArea : " << vps[i]->getArea() 71 << endl; 72 } 73 } 74 75 int main() 76 { 77 Vec_PSHAPE vecShape; 78 79 vecShape.push_back( new CCircle(3.2) ); 80 vecShape.push_back( new CRectangle(3.5, 4.6) ); 81 vecShape.push_back( new CCircle(7.8) ); 82 vecShape.push_back( new CRectangle(1.5, 8.6) ); 83 vecShape.push_back( new CRectangle(4., 3.) ); 84 vecShape.push_back( new CCircle(14.8) ); 85 86 fnPrint( vecShape, "------ before sort ------" ); 87 // sorting 88 sort( vecShape.begin(), vecShape.end(), SFOCompareShapeArea() ); 89 // after sort 90 fnPrint( vecShape, "------ after sort ------" ); 91 92 for_each( vecShape.begin(), vecShape.end(), SDeleteObject() ); 93 94 return EXIT_SUCCESS; 95 } |
---------- run - C/C++ ----------
------ before sort ------
Shape type : Circle Area : 20.1062
Shape type : Rectangle Area : 16.1
Shape type : Circle Area : 49.0088
Shape type : Rectangle Area : 12.9
Shape type : Rectangle Area : 12
Shape type : Circle Area : 92.9911
------ after sort ------
Shape type : Rectangle Area : 12
Shape type : Rectangle Area : 12.9
Shape type : Rectangle Area : 16.1
Shape type : Circle Area : 20.1062
Shape type : Circle Area : 49.0088
Shape type : Circle Area : 92.9911
출력 완료 (0초 경과)
출력 결과를 보시면 알겠지만 class type과는 상관없이 면적 계산한 값을 기준으로 오름차순 정렬이 됩니다.
CShape 코드 내용과 하위 객체의 내용이 조금 바꾸었습니다.
여기서 하나 알아 두면 좋은 것이 base class의 생성자를 protected로 넣는 것입니다.
현재 코드는 CShape 클래스가 순수가상함수를 갖고 있기 때문에 이렇게 할 이유는 없습니다만,
그렇지 않을 경우, base 클래스를 클라이언트에서 객체를 만들어 사용하면 안되는데 가능하게 디자인 된 경우
문제발생 예방차원에서 base class를 생성하지 못하게 하는 방법으로 사용합니다.
이것과 유사한 방식으로 복사생성자와 대입연산자(operator=)을 private을 넣어 반드시 명시적으로만 생성하도록
하는 방법도 자주 사용됩니다.
다른건 처음 코드와 대부분 유사한데 sort에서 사용하는 predicator 가 SFOCompareShareArea라는
function object 로 만들었는데 binary_function을 상속하여 구현한게 낯선 분들도 있을 겁니다.
unary_function과 binary_function의 자세한 설명은 굉장히 많네요.. ^^;
참고해 보세요.
이번 주요 내용을 정리해 보면 STL 컨테이너에 pointer type을 요소로 할 때는 컨테이너가 사라지기전에
반드시 삭제를 해야하고 이를 편하게 하는 방법은 for_each와 function object를 이용하는 것이고,
좀 더 확장하여 function object를 binary_function 상속하여 sort 등 처리를 간단하게 하는것을 보셨습니다.
'C C++ - STL' 카테고리의 다른 글
STL - 16 bitset 활용 (0) | 2014.02.13 |
---|---|
STL - 15 STL contaner list 응용 - Composite pattern 활용 (0) | 2014.02.13 |
STL - 14 STL container map 응용 - Singleton pattern 적용한 Config 처리 (0) | 2014.02.13 |
STL - 13 allocator (0) | 2014.02.13 |
STL - 12 auto_ptr (0) | 2014.02.13 |