본문 바로가기
프로그래밍 이야기/C++ 기초

[C++]Design Pattern - STL과 Adapter

by Mulder5 2020. 12. 30.
반응형

시나리오

STL에 stack이 존재하지만 stack을 한번 만들어보자. 기존 list는 양쪽 방향으로 쓰기가 가능하다. 이 양쪽 방향 쓰기를 단방향 쓰기로 만들어준다면 stack처럼 사용할 수 있을 것이다. 또한 list의 함수 이름을 stack처럼 보이도록 변경해주면 될 것이다.

#include <iostream>
#include <list>
#include <vector>
#include <deque>

using namespace std;

template<typename T> class Stack : public list<T>
{
public:
    void push(const T& a) { list<T>::push_back(a); }
    void pop() { list<T>::pop_back(); }
    T& top() { return list<T>::back(); }
};

int main()
{
    Stack<int> s;
    s.push(10);
    s.push(20);
    cout << s.top() << endl;
}

위의 구현에서 list::push_back()과 pop_back()이 각각 push()와 pop()으로 변경 되었다. 하지만 list를 그대로 상속 했기에 Stack 타입임에도 불구하고 다음과 같이 list의 멤버 함수를 직접 호출이 가능한 상태이다. 

int main()
{
    Stack<int> s;
    s.push(10);
    s.push(20);
    cout << s.top() << endl;

    // stack으로 약속하고 사용하려고 만들었는데 사용자가 list의 함수를 사용한다면? 
    // 문제 없이 사용할 수 있다. 이것은 문제다.
    s.push_front(20);
}

사용자가 Stack 객체에서 list의 멤버들을 사용할 수 없도록 할 수 없을까?

해결 방법 1

list를 private로 상속한다. 

// 해결방법1 (private 상속)
// list의 속성을 받을 수 있는 장점
template<typename T> class Stack1 : private list<T>
{
public:
    void push(const T & a) { list<T>::push_back(a); }
    void pop(const T & a) { list<T>::pop_back(a); }
    T& top() { return list<T>::back(); }
};

이 경우 push_front를 호출하면 에러가 발생하여 사용자는 이 함수를 사용할 수 없다. 

 Stack1<int> s1;
 s1.push(10);
 s1.push(20);
 //s1.push_front(20); // error!

 

해결 방법 2

list를 포함관계로 만들어준다. 

// 해결방법2 (포함으로 변경)
// 완전히 List의 멤버를 폐쇄적으로 사용할 수 있다. 
template<typename T> class Stack2
{
    list<T> lst;
public:
    void push(const T& a) { lst.push_back(a); }
    void pop() { lst.pop_back(); }
    T& top() { return lst.back(); }
};

이 경우, list의 객체가 완전히 감추어졌기 때문에 외부에서 push_front를 호출 할 수 없다.

 Stack2<int> s2;
 s2.push(10);
 s2.push(20);
 // s2.push_front(20); //error

 

사용 편의를 위해 확장

// Stack2의 확장, 
// 어떤 컨테이너를 기반으로 확장할 것인지도 외부에서 결정해줄 수 있다. (기본값 deque제공)
template<typename T, typename C = deque<T> > class Stack3
{
    C lst;
public:
    void push(const T& a) { lst.push_back(a); }
    void pop() { lst.pop_back(); }
    T& top() { return lst.back(); }
};

Template 의 두번째 인자로 컨테이너의 타입도 결정해줄 수 있다. 지정해 주지 않는다면 deque로 사용된다. 이에 대한 사용은 다음과 같다.

  Stack3<int, list<int>> s3;
  Stack3<int, vector<int>> s4;
  Stack3<int> s5;

 

Container Adapter

Sequence Container의 인터페이스를 수정해서 stack, queue, priority_queue를 제공한다.
위의 예제에서 "해결방법2"의 Stack2와 "사용 편의를 위한 확장"의 Stack3의 경우 포인터를 받지 않으므로 클래스 어뎁터이다.

반응형

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

[Design Pattern] Bridge  (0) 2021.02.02
[DesignPattern] Facade  (0) 2021.01.19
[C++]Design Pattern - Adapter  (0) 2020.12.30
[게임이론] 벡터-내적의 활용  (0) 2020.12.26
[C++]Design Pattern - Decorator Pattern  (0) 2020.12.18