2009/03/11 17:16

strategy pattern

크리에이티브 커먼즈 라이선스
Creative Commons License
간만에.. 복습을 좀 했습니다.  사람은 망각의 동물이라자나요...-_-ㅋ
함수명이나 동작에 특별한 의미는 없습니다. 그저 구조를 복습했을 뿐이에요.

#include <iostream>

class strategy_interface{
public:
    virtual int calc(int i, int j) = 0;
};

class plus : public strategy_interface{
public:
    int calc(int i, int j){
        return i + j;
    }
};

class minus : public strategy_interface{
public:
    int calc(int i, int j){
        if(i > j)
            return i-j;
        else
            return j-i;
    }
};

class people{
private:
    int m_year;
    int m_tall;
    strategy_interface *m_strategy;
public:
    explicit people(int year, int tall, strategy_interface *strategy) : m_year(year), m_tall(tall), m_strategy(strategy){}
    ~people(){ delete m_strategy; }
    void calc(){
        std::cout << m_strategy->calc(m_year, m_tall) << std::endl;
    }
};


int main(int argc, char* argv[])
{
    people man(39, 187, new plus());
    man.calc();

    people woman(20, 170, new minus());
    woman.calc();
   
    return 0;
}

저작자 표시 비영리 변경 금지
Trackback 0 Comment 0

Trackback : http://itworlds.tistory.com/trackback/35 관련글 쓰기

2009/02/16 17:34

템플릿 특수화

크리에이티브 커먼즈 라이선스
Creative Commons License

1. 템플릿의 특수화

템플릿을 이용하면 어떠한 형태(클래스 및 기본제공 형식)를 사용하더라도 같은 소스 코드를 사용 할 수 있습니다. 이것은 굉장히 편리한 기술이죠. 하지만 특별한 경우에만 다른 코드를 쓰고 싶은 경우도 있습니다. 예를들어 다른 모든 형식에는 공통의 소스코드를 사용하더라도 int만은 다른 코드를 사용하고 싶은 경우입니다.

위와 같은 경우 사용할 수 있는 것이 템플릿 특수화 입니다. 그럼 간단한 코드부터 시작 해 보겠습니다.

#include <iostream>

//일반 템플릿 클래스
template <class T>
class CSample
{
public:
	CSample(){ std::cout << "class T version" << std::endl; }
};

// int전용 특수화 클래스
template <>
class CSample<int>
{
public:
	CSample(){ std::cout << "int version" << std::endl; }
};


int main()
{
	CSample<int> obj1;
	CSample<double> obj2;

	return 0;
}

obj1은 템플릿 인자로 int를, obj2는 double을 인자로 넘겨 주었습니다.    위의 소스코드 결과는 다들 보셨겠지만 아래와 같습니다. 

int version 
class T version

이것이 바로 템플릿 특수화 입니다. 간단하지요?? 

그럼 부연 설명을 해 보겠습니다. 먼저 template<>에서 보시듯이 아무것도 정의하지 않은 <>을 사용하면, 이 template가 특수화 임을 나타냅니다.   그리고 클래스 이름 옆에 어떤 형식에 대하여 특수화를 할 것인가를 기술해 줍니다.  위의 예제를 들어보면 int에 대한 특수화이므로  int를 적어 주었습니다. 

그리고 일반 템플릿의 정의가 특수화 보다 먼저 와야 합니다. 그렇지 않으면 컴파일 에러가 발생합니다. 

또한 템플릿 특수화는 함수에도 적용이 가능합니다.

 template void function(const T& t); //일반 템플릿 함수
 template <> void function(const int& t); // int 특수화 템플릿 함수

 특별하게 다른 것은 없습니다. 템플릿 클래스의 특수화와 같은 개념입니다.  아, 템플릿 함수의 경우에는 아래와 같이 선언해도 문제가 되지 않습니다.

 template <> void function(const int& t); // int 특수화 템플릿 함수 



2. 템플릿의 부분 특수화 

말그대로 템플릿의 특수화를 부분적으로만 하는 것입니다. 아래 코드를 보시길 바랍니다. 
#include <iostream>

//일반 템플릿 클래스
template <class T>
class CSample
{
public:
	CSample(){ std::cout << "class T version" << std::endl; }
};

//포인터 전용 부분 특수화 클래스
template <class T>
class CSample<T*>
{
public:
	CSample(){ std::cout << "pointer version" << std::endl; }
};


int main()
{
	CSample<int> obj1;
	CSample<int*> obj2;

	return 0;
}

실행 결과는 아래와 같습니다. 
class T version 
pointer version 

포인터에 대해서만 특수화를 했습니다. 그래서 부분 특수화 입니다. 별도의 설명이 필요할 정도로 어렵지 않죠?? 

이 부분 특수화는 vc6.0에서는 컴파일이 되지 않습니다. .net2003이후 버젼 그리고 Borland C++ Compiler5.5.1에서는 됩니다. 참고 해 주세요.

Trackback 0 Comment 0

Trackback : http://itworlds.tistory.com/trackback/34 관련글 쓰기

2009/01/28 17:41

strategy pattern sample code

크리에이티브 커먼즈 라이선스
Creative Commons License
스토리지 패턴이요?? 전 그냥 알고리즘을 갈아끼우기 편한 패턴 이라고 외웠습니다.. 실제로도 그렇구요. 구글님에게 검색하면 아주 많은 좋은 자료들이 나올 겁니다.

위 처럼 쉽게 알고리즘 갈아끼우기 패턴이라고 외워놓고 샘플코드를 몇개 작성해 보았습니다.

sample 1  -  매우 간단합니다. 템플릿을 적용해 봤습니다.
#include <iostream>

using namespace std;

class algorithm1{
public:
    void show() const{
        cout << "algorithm1" << endl;
    }

};

class algorithm2{
public:
    void show() const{
        cout << "algorithm2" << endl;
    }
};


template<typename T>
class sample{
public:
    void call_show(){
        strategy.show();
    }
private:
    T strategy;
};


int main(int argc, char* argv)
{
    sample<algorithm1> s1;
    s1.call_show();

    sample<algorithm2> s2;
    s2.call_show();

    return 0;
}


sample 2 - 같은 mystyle이라도 알고리즘이 다르게 적용되는 것을 볼 수 있습니다.  하악하악 클래스의 인자값과 결과값을 천천히 비교해 보시면 금방 아실 수 있으실 겁니다.
 #include <iostream>

using namespace std;

//stragety pattern1
enum KOREAN { KIMTH, JUNJH, MUNGY }; //김태희, 전지현, 문근영.....-_-
enum JANPANESE { HIRO, SORA, ARA }; //히료스에, 소라아오이, 아라각키....-_-


class ha_ak_ha_ak{
private:
    int k,j;
public:
    explicit ha_ak_ha_ak( const int& kor, const int& jap ) : k(kor), j(jap){}
    virtual ~ha_ak_ha_ak(){}
    virtual char* final_answer() {
        if(k == KIMTH)
            return "i like you!!!";
        else if(j == ARA)
            return "i like you too!!!";
        else
            return "Whooo~~~";
   
    }
};


class ha_ak_ha_ak2{
private:
    int k,j;
public:
    explicit ha_ak_ha_ak2( const int& kor, const int& jap ) : k(kor), j(jap){}
    virtual ~ha_ak_ha_ak2(){}
    virtual char *final_answer(){
        if(k == MUNGY)
            return "u are too younger, but i like u!!!";
        else if(j == SORA)
            return "Ha~ak Ha~ak!!!";
        else
            return "Whooo~~~";

    }
};


template<typename T>
class mystyle{
public:
    explicit mystyle(T t): strategy(t){}
    ~mystyle(){}
    void choice(){
        cout << strategy.final_answer() << endl;   
    }
private:
    T strategy;
};


int main(int argc, char* argv[]){
    {
        ha_ak_ha_ak2 h2(MUNGY, SORA);
        mystyle<ha_ak_ha_ak2> m1(h2);
        m1.choice();
    }
    {
        ha_ak_ha_ak2 h2(SORA, SORA);
        mystyle<ha_ak_ha_ak2> m1(h2);
        m1.choice();
    }
    {
        ha_ak_ha_ak h2(ARA, SORA);
        mystyle<ha_ak_ha_ak> m1(h2);
        m1.choice();
    }
    {
        ha_ak_ha_ak h2(MUNGY, KIMTH);
        mystyle<ha_ak_ha_ak> m1(h2);
        m1.choice();
    }
    return 0;
}


잊지 말자구요. 스토리지는 알고리즘 교환을 위한 패턴입니다.



저작자 표시 비영리 변경 금지
Trackback 0 Comment 0

Trackback : http://itworlds.tistory.com/trackback/33 관련글 쓰기

2009/01/09 15:28

idiom/pimpl 이란?

크리에이티브 커먼즈 라이선스
Creative Commons License
질문!!
 - 파일들의 의존 관계가 복잡 해 질수록, 컴파일 시간이 길어져서 짜증이 나는 상태.. 뭔가 그럴듯하고 있어 보이는 방법으로 해결 할 수 없을까요??

답!
 - 없을리가 있나요. 천천히 아래의 글을 읽어 보세요.

보통의 코드

Object.h
#ifndef INCLUDE_GUARD_OBJECT_H
#define INCLUDE_GUARD_OBJECT_H

#include <string>

class Object{
public:
    explicit Object(const std::string& name);
    ~Object();
    void print();

private:
    std::string name;
};


#endif  // INCLUDE_GUARD_OBJECT_H

Object.cpp
#include <iostream>
#include "Object.h"

Object::Object(const std::string &name):name(name){}

Object::~Object(){}

void Object::print(){
    std::cout << name << std::endl;
}

main.cpp
#include "Object.h"

int main(int argc, char* argv[])
{
    Object object("instance");
    object.print();

    return 0;
}
위의 프로젝트에서 object::print() 함수에 변화가 있을 경우에는 컴파일이 필요한 화일은 Object.cpp뿐입니다. 클라이언트 코드(main.cpp)는 재 컴파일 할 필요가 없지요. 재 링크만 하면 됩니다.

하지만 object클래스 멤버 변수가 추가된 다면???

멤버 변수 추가
Object.h
#ifndef INCLUDE_GUARD_OBJECT_H
#define INCLUDE_GUARD_OBJECT_H

#include <string>

class Object{
public:
    explicit Object(const std::string& name);
    ~Object();
    void print();

private:
    std::string name;
    int count;
};


#endif  // INCLUDE_GUARD_OBJECT_H

Object.cpp
#include <iostream>
#include "Object.h"

Object::Object(const std::string &name):name(name), count(10){}

Object::~Object(){}

void Object::print(){
    std::cout << count << " @ " << name << std::endl;
}

main.cpp
#include "Object.h"

int main(int argc, char* argv[])
{
    Object object("instance");
    object.print();

    return 0;
}
object클래스의 멤버함수에 변화가 생기면, 그 멤버 변수가 private 일지라도 클라이언트 코드의 재 컴파일을 해야하는 상황이 옵니다.  실제로 변화가 생긴건 클라이언트 코드(main.cpp)와는 관계가 없는데 말이죠... 이러한 의존 관계를 피하고 싶다는 말입니다.

자 그럼 어떻게 해야 할까요???   그건 바로 object 멤버 변수를 포인터로 은폐시키는 겁니다.  이해가 안되신다구요?? 소스를 보도록 하지요.

idiom 추가
Object.h
#ifndef INCLUDE_GUARD_OBJECT_H
#define INCLUDE_GUARD_OBJECT_H

#include <string>

class Object{
public:
    explicit Object(const std::string& name);
    ~Object();
    void print();

private:
    struct ObjectImpl;
    ObjectImpl *pimpl;
};


#endif  // INCLUDE_GUARD_OBJECT_H

Object.cpp
#include <iostream>
#include "Object.h"

struct Object::ObjectImpl {
    explicit ObjectImpl(const std::string& name) : name(name), count(0) {}
  std::string name;
  int         count;
};

Object::Object(const std::string& name) : pimpl(new ObjectImpl(name))
{
}

Object::~Object()
{
  delete pimpl;
}

void Object::print()
{
  std::cout << "[" << ++pimpl->count << "] " << pimpl->name << std::endl;
}

main.cpp
#include "Object.h"

int main(int argc, char* argv[])
{
    Object object("instance");
    object.print();

    return 0;
}

이로써 object::objectImpl구조체 변수에 변화가 생겨도 클라이언트 코드는 재 컴파일 할 필요가 없어 졌습니다!!!!
그리고 뭔가 있어 보이는 코드가 되었고(실제로도 뭔가 있죠! 아주 좋은 일이) 다른 사람 한테도 자랑할 만한 기술을 하나 획득 했습니다!!!

이게 바로 pimpl idoim불리는 기술 입니다.

Exceptional C++라는 책에서 소개된 기술이고요. 아주 유용하게 쓰인답니다.


참고로......
//--------------------------------------------------------
//header.h
class Imp;
class TestPImpl
{
  Imp*pImpl;
public:
  TestPImpl();
  void Func();
};

//cpp.cpp
class Imp
{
  int i;
public:
  void Func()
  {
   i++;
  }
};

TestPImpl::TestPImpl()
{
 pImpl = new Imp;
}
void TestPImpl::Func()
{
 pImpl->Func();
}

//--------------------------------------------------------
아래와 같은 방법으로도 인터페이스 구현은 가능합니다.
//--------------------------------------------------------
//header.h
class VirtualBase
{
public:
  virtual void Func() =0;
};
VirtualBase*CreateVirtualBase();

//cpp.cpp
class VirtualDeri : public VirtualBase
{
  int i;
public:
  void Func()
  {
   i++;
  }
};

VirtualBase*CreateVirtualBase()
{
  return new VirtualDeri;
}
//--------------------------------------------------------
이 방법이 보기에도 편하고
속도도 빠릅니다.
//--------------------------------------------------------
//main.cpp
int main()
{
  const int MAX_CNT = 10000000;

  VirtualBase*v = CreateVirtualBase();
  long startTime = timeGetTime();
  for(int i=0;i<MAX_CNT;i++)
   v->Func();
  printf("Vertial:[%d]\n",timeGetTime() - startTime);
  TestPImpl*p = new TestPImpl();
  startTime = timeGetTime();
  for(int i=0;i<MAX_CNT;i++)
   p->Func();
  printf("pImpl:[%d]\n",timeGetTime() - startTime);
}

결과
>Virtual[336]
>pImp[594]

http://okwave.jp/qa4011938.html
가상 함수를 쓰는 방법은 effective c++에서도 소개가 된 기술인데요.
idoim와의 가장 큰 차이는  idoim은 동적 작업이 가능하다는 겁니다.

현장에서 많이 쓰이는 방법 중 하나를 또 정리해 봤습니다. 정리 했으니 앞으론.... 일하는데 idiom때문에 해메면 안되겠죠.




저작자 표시 비영리 변경 금지
Trackback 0 Comment 1

Trackback : http://itworlds.tistory.com/trackback/32 관련글 쓰기

  1. nimbus9030 2009/01/09 15:31 address edit & del reply

    참고1 http://www.gotw.ca/gotw/028.htm

2009/01/08 17:47

[boost]scoped_ptr

크리에이티브 커먼즈 라이선스
Creative Commons License
C++에서 메모리 관련 작업은 해도 해도 끝이 없지 않나 싶어요. auto_ptr, shared_ptr에 대해선 요전에 포스팅한 내용을 참고 하시고 오늘은 scoped_ptr이라는 놈에 대하여 적어 볼게요.

개념 줄줄이 적어봐야 외우기 힘드니 줄여서!

1. 포인터의 공유나 이동을 허용하지 않는 auto_ptr
2. STL 컨테이너 사용 불가
3. idiom구현시 사용 - 이거 어렵지만 정리 할 겁니다.

이렇게 간단하게 요약 됩니다.

auto ptr, shared_ptr설명할 때 들었던 예제인데요. 똑같은 방식이지만 에러 나요. 이유는 위에 있지요.
    typedef boost::scoped_ptr<string>ScopeStrptr;

    vector<ScopeStrptr> spv;

    ScopeStrptr sp(new string("test5"));

    //ERROR!!!
    //spv.push_back( sp );





저작자 표시 비영리 변경 금지
Trackback 0 Comment 0

Trackback : http://itworlds.tistory.com/trackback/31 관련글 쓰기