간만에.. 복습을 좀 했습니다. 사람은 망각의 동물이라자나요...-_-ㅋ
함수명이나 동작에 특별한 의미는 없습니다. 그저 구조를 복습했을 뿐이에요.
함수명이나 동작에 특별한 의미는 없습니다. 그저 구조를 복습했을 뿐이에요.
#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;
}
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;
}
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;
}
template <> void function(const int& t); // int 특수화 템플릿 함수
#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;
}실행 결과는 아래와 같습니다.
스토리지 패턴이요?? 전 그냥 알고리즘을 갈아끼우기 편한 패턴 이라고 외웠습니다.. 실제로도 그렇구요. 구글님에게 검색하면 아주 많은 좋은 자료들이 나올 겁니다.
위 처럼 쉽게 알고리즘 갈아끼우기 패턴이라고 외워놓고 샘플코드를 몇개 작성해 보았습니다.
sample 1 - 매우 간단합니다. 템플릿을 적용해 봤습니다.
sample 2 - 같은 mystyle이라도 알고리즘이 다르게 적용되는 것을 볼 수 있습니다. 하악하악 클래스의 인자값과 결과값을 천천히 비교해 보시면 금방 아실 수 있으실 겁니다.
잊지 말자구요. 스토리지는 알고리즘 교환을 위한 패턴입니다.
위 처럼 쉽게 알고리즘 갈아끼우기 패턴이라고 외워놓고 샘플코드를 몇개 작성해 보았습니다.
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; } |
잊지 말자구요. 스토리지는 알고리즘 교환을 위한 패턴입니다.
질문!!
- 파일들의 의존 관계가 복잡 해 질수록, 컴파일 시간이 길어져서 짜증이 나는 상태.. 뭔가 그럴듯하고 있어 보이는 방법으로 해결 할 수 없을까요??
답!
- 없을리가 있나요. 천천히 아래의 글을 읽어 보세요.
보통의 코드
Object.h
Object.cpp
main.cpp
하지만 object클래스 멤버 변수가 추가된 다면???
멤버 변수 추가
Object.h
Object.cpp
main.cpp
자 그럼 어떻게 해야 할까요??? 그건 바로 object 멤버 변수를 포인터로 은폐시키는 겁니다. 이해가 안되신다구요?? 소스를 보도록 하지요.
idiom 추가
Object.h
Object.cpp
main.cpp
이로써 object::objectImpl구조체 변수에 변화가 생겨도 클라이언트 코드는 재 컴파일 할 필요가 없어 졌습니다!!!!
그리고 뭔가 있어 보이는 코드가 되었고(실제로도 뭔가 있죠! 아주 좋은 일이) 다른 사람 한테도 자랑할 만한 기술을 하나 획득 했습니다!!!
이게 바로 pimpl idoim불리는 기술 입니다.
Exceptional C++라는 책에서 소개된 기술이고요. 아주 유용하게 쓰인답니다.
참고로......
idoim와의 가장 큰 차이는 idoim은 동적 작업이 가능하다는 겁니다.
현장에서 많이 쓰이는 방법 중 하나를 또 정리해 봤습니다. 정리 했으니 앞으론.... 일하는데 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:
std::string name;
};
#endif // 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;
}
#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)는 재 컴파일 할 필요가 없지요. 재 링크만 하면 됩니다. int main(int argc, char* argv[])
{
Object object("instance");
object.print();
return 0;
}
하지만 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
#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;
}
#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)와는 관계가 없는데 말이죠... 이러한 의존 관계를 피하고 싶다는 말입니다.int main(int argc, char* argv[])
{
Object object("instance");
object.print();
return 0;
}
자 그럼 어떻게 해야 할까요??? 그건 바로 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
#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;
}
#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;
}
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++에서도 소개가 된 기술인데요. //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
idoim와의 가장 큰 차이는 idoim은 동적 작업이 가능하다는 겁니다.
현장에서 많이 쓰이는 방법 중 하나를 또 정리해 봤습니다. 정리 했으니 앞으론.... 일하는데 idiom때문에 해메면 안되겠죠.
C++에서 메모리 관련 작업은 해도 해도 끝이 없지 않나 싶어요. auto_ptr, shared_ptr에 대해선 요전에 포스팅한 내용을 참고 하시고 오늘은 scoped_ptr이라는 놈에 대하여 적어 볼게요.
개념 줄줄이 적어봐야 외우기 힘드니 줄여서!
1. 포인터의 공유나 이동을 허용하지 않는 auto_ptr
2. STL 컨테이너 사용 불가
3. idiom구현시 사용 - 이거 어렵지만 정리 할 겁니다.
이렇게 간단하게 요약 됩니다.
auto ptr, shared_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 );
vector<ScopeStrptr> spv;
ScopeStrptr sp(new string("test5"));
//ERROR!!!
//spv.push_back( sp );




Prev

Rss Feed