-
형태별 분류
- 일반 포인터
- Container Iterator
- Stream Iterator
- Inserter Iterator
- 기능별 분류
=*i
*i=
++i
i++
--i
i--
i[n]
i+n
i-n
i+=n
i-=n
입력 반복자
O
O
O
출력 반복자
O
O
O
정방향 반복자
O
O
O
O
양방향 반복자
O
O
O
O
O
O
임의 접근 반복자
O
O
O
O
O
O
O
O
O
O
O
-
5가지 종류로 구분하는 이유
- 알고리즘 함수가 요구하는 반복자에 따라 알고리즘 함수를 구분하기 위해서이다.
- 프로그램 ? Container ( 출력 )
- Container ? 프로그램 ( 입력 )
-
Example
- find( first, last, value ); // 최소한 입력 반복자
- reverse( first, last ); // 최소한 양방향 반복자
-
sort( first, last ); // 퀵소트라면 최소한 임의접근 반복자.
// 입.출력,정.양방향 은 내부에 sort 함수를 가지고 있다.
'C++'에 해당되는 글 84건
- 2007/08/02 8.1(수) C++ - 반복자 분류
- 2007/08/02 8.1(수) C++ - 반복자(iterator)의 개념. 용어
- 2007/08/02 8.1(수) C++ - template 전문화
- 2007/08/02 8.1(수) C++ - typename
- 2007/08/02 8.1(수) C++ - template 파라미터
- 2007/08/02 8.1(수) C++ - 클래스 template, 멤버함수 template
- 2007/08/02 8.1(수) C++ - 함수 template
- 2007/08/02 7.31(화) C++ - Composite 패턴
- 2007/08/02 7.31(화) C++ - 추상클래스
- 2007/08/02 7.31(화) C++ - typeid, type_info
// 1. 개념. 용어
반복자는(Iterator)는 포인터와 유사한 객체로서 STL의 알고리즘 함수가 컨테이너에 저장된 객체들의 시퀀스를 순회 할 때 사용한다. 컨테이너와 알고리즘 함수의 사이를 연결해주는 중간자로서의 역할을 담당하기도 한다. 반복자 덕택에 저장방식(자료구조)에 관계없이 동일한 알고리즘 함수를 구현 할 수 있는 것이다.
STL의 각 컨테이너는 자신의 요소를 가르키는 반복자를 가지고 있다. 각 컨테이너의 begin(), end() 함수는 자신이 지닌 첫번째 요소와 마지막 다음(past-the-end) 요소를 가르키는 반복자를 리턴한다.
반복자 구간 ? 시퀀스의 시작을 가르키는 first, 마지막 다음을 가르키는 last의 반복자 한 쌍. 반복자 구간은 [first, last] 처럼 표기한다. 또한, first에서 시작해서 operator++ 연산으로 도달 가능한 경우 유효(Valid, 도달가능-reachable)한 구간이라고 한다. 모든 STL의 알고리즘은 넘겨 받은 구간이 유효하다는 가정하에 작업을 수행한다. 또한 아무 요소도 없는 구간은
first == last
가 되는데 이를 빈 구간(empty)라고 한다. 빈 구간도 유효한 구간이다.
Past the end ? Container의 end() 함수를 통해서 얻게 되는 iterator는 마지막 요소가 아니라 마지막 다음을 가르키는데 이를 보통 past-the-iterator라고 부른다. 이 iterator를 참조하는 것은 오류이다.
// 2. 반복자의무효화
void main()
{
vector<int> v(10);
v[0] = 100;
vector<int>::iterator p = v.begin();
cout << *p << endl;
// v.resize(20); // 반복자를 무효화 시킨다.p가 가리키는 곳을 재할당
v.resize(5); // 무효화 될 가능성이 있다. 메모리를 줄이기만 한다.
cout << *p << endl;
}
///////////////////////////////////////////////////////////////////
// 3. 반복자의 활용
void main()
{
list<int> s;
s.push_back(10);
s.push_back(20);
s.push_back(30);
s.push_back(40);
slist<int>::iterator p1 = s.begin();
cout << *p1 << endl;
slsit<int>::iterator p2 = s.end();
reverse( p1, p2 )
}
///////////////////////////////////////////////////////////////////
void main()
{
vector<list<int> > st(10); // hash table
st[0].push_back(10);
vector<string> v(10);
v[0][0] = 'a';
string s = "ahfsdhksdfkshdfkjhsdkfhsdkfh";
sort(s.begin(), s.end());
reverse( s.begin(), s.end());
replace( s.begin(), s.end(), 'f', '-');
cout << s << endl;
}
// 반복자의분류- 5가지
int main()
{
list<int> s; // 양방향반복자
sort( s.begin(), s.end()); // 임의접근이어야한다.
s.sort(); // Quick 이아닌버블이나selection 으로구현됨.
int x[10] = { 1,2,3,4,5,6,7,8,9,10};
int y[10];
copy( x, x+10, y); // x ~ x+10을y로복사한다.
const int x[10] = { 1,2,3,4,5,6,7,8,9,10};
int k = *x; // ok..
*x = 20; // error
slist<int> s;
// s에요소추가...
slist<int>::iterator p = s.begin();
int k = *p; // 입력
*p = 30; // 출력
++p; // ok...
--p; // 될까?? 안된다. single linked list는 전방향!!
}
// template 전문화가 요즘들어 인기를 얻고 있는 이유. - 메타의 세계
// 컴파일시간에 어떤일을 수행하게 하는 기법. 컴파일시간은 오래 걸리지만
// 실행시간이 단축된다. 주로 전문화를 사용해서 컴파일 재귀호출을 사용한다.
// 하지만 컴파일 역량이 있기 때문에 9번이상?? 은 안된다. 왜그렇까..컴파일 하기 싫은가..
template<int n> class Pow
{
public:
enum { result = n * Pow<n-1>::result };
};
template<> class Pow<0>
{
public:
enum { result = 1 };
};
void main()
{
int n = Pow<5>::result;
cout << n << endl;
}
// Template 전문화( 특화, Specialization ).
template<typename T, typename T2> class Test {}; // 1
template<typename T> class Test<T, int> {}; // 2
Test<int, double> t1; // 1
Test<int, int> t2; // 2
// Primary Template
template<typename T> class Stack
{
T* buf;
};
// 전문화 Template
template<> class Stack<int>
{
};
// 부분전문화- 모든 포인터는 이 클래스 사용
template<typename T> class Stack<T*>
{
T* buf;
};
void main()
{
Stack<int> s;
}
#include <iostream>
using namespace std;
// typename 문법이야기
// 1.2 의 중복문제가 발생해서 이를 해결하고자 typename 키워드를 만들었다.
// 그러고 보니 class T 라는 것의 의미가 명확하지가 않는 것을 알 수가 있었다.
// class T는 int와 같은 타입도 받을 수 있기 때문에 typname으로 바뀌게 된 것이다.!!!
// 하지만 class는 여전히 지원된다.
template<typename T> void foo( T a )
{
typename T::B* p;
// 1. T안에 내포클래스로 B가 있는데 그 포인터 p를 선언.
// 2. T안에 B라는 static 변수가 있는데 곱하기 p를 한다.
}
class A
{
public:
static int B;
class B
{
};
};
void main()
{
A a;
foo( a );
}
////////////////////////////////////////////////////////////////////
// 디폴트 인자
template<typename T = int, int n = 10> class Stack
{
T buf[n];
};
void main()
{
Stack<int, 10> s;
Stack<int> s2;
stack<> s3; // 모두 default 사용
}
#include <iostream>
#include <vector>
using namespace std;
// template 파라미터의종류
// 1. type
// 2. 상수( 정수와포인터상수만가능) 파라미터.
// 3. template temlpate 파라미터- template 이름을 넘긴다???
// vector는 디폴트 인자가3 개가 있으므로 넘기기 힘들다.
// 그래서 새롭게 만든 template를 넘겨보자!! VECTOR
template<typename T> class VECTOR
{
};
template<template<typename> class A, typename T> class Stack
{
A<T> v;
public:
};
int main()
{
Stack<VECTOR, int> s;
return 0;
}
////////////////////////////////////////////////////////////////////
template <typename T, int n, template<typename> class A> class Stack
{
A<T> v; // 결국 vector<int> v
T buf[n]; // 배열첨자에 템플릿인자 상수는 가능하다.
};
void main()
{
vector<int> a;
vector b;
Stack<int, 10, vector> s;
int n = 10;
//Stack<int, n> s2; // error
}
////////////////////////////////////////////////////////////////////
// 클래스template 2
typedef vector<vector<int> > Matrix;
void main()
{
//vector<vector<int> > m(3, 3);
Matrix m(3, 3);
m[0][0] = 10;
vector<int> v(10, 3);
v[0] = 10;
}
////////////////////////////////////////////////////////////////////
template<typename T> class Stack
{
};
void main()
{
Stack<int> s;
Stack<Stack<int> > s2; // >> 와 혼동 되지 않도록 해야 한다.
}
// 클래스 template의 기본.
template <typename T> class Stack
{
T* buf;
public:
Stack() {} // 1. ok. 생성자이름은클래스이름
//Stack<T>() {} // 2. 에러
// 복사생성자의모양
Stack( const Stack<T>& ) {}
void push ( T a );
};
// 클래스template의멤버함수를외부에구현하려면
template<typename T> void Stack<T>::push( T a )
{
}
void main()
{
Stack<int> s1;
// Stack s2; // error. Stack template의이름이지Type이아니다.
// type은Stack<T> 이다.
}
// 멤버 함수 template를 활용해보자.!!!
template<typename T> class Stack
{
public:
Stack(){}
template<typename U> explicit Stack(const Stack<U>&)
{
}
explicit Stack(const Stack<T>&)
{
}
};
void main()
{
Stack<int> s1;
Stack<int> s2(s1);
Stack<int> s3;
Stack<double> s4(s3);
}
#include <iostream>
using namespace std;
// 1. 함수template
// (1) instantiation : T -> 특정 type으로 변경되는 과정.
// (2) template 의원리: Code Generation ->
// 단점: Code Bloat(코드가커질수있다.),2번 컴파일 한다.
// (3) template 은 2번문법을 확인하게 된다.
// (4) internal linkage를가진다.(완전한 구현체를 가져야 한다.),다른파일에 정의하면 에러
// 클래스template를 만들때에서 선언과 구현 모두 헤더에 넣어야 한다.
// (5) 함수template 도 overloading 된다.
template <typename T> T Max( T a, T b ) // 1
{
return a < b ? b : a;
}
int Max( int a, int b ) // 2
{
return a < b ? b : a;
}
void main()
{
cout << Max( 1, 2 ) << endl; // 2
cout << Max( 3.4, 3.2 ) << endl; // 1
// 2. 6.0은에러.int버전이없다면T가ambigous 하다는에러.
cout << Max( 65, 'B' ) << endl;
}
#include <iostream>
#include <string>
#include <vector>
using namespace std;
// 폴더와File의공통의부모
class Item
{
string name;
public:
Item( string s ) : name ( s ) {}
// 상수함수로만들어야상수함수에서불러올수있다.
string GetName() const { return name; }
virtual int GetSize() = 0;
// Getname을상수함수로만들자!!
{
cout << prefix << GetName() << endl;
}
};
class File : public Item
{
int size;
public:
// 부모클래스에디폴트생성자가없으므로명시적으로부모생성자를호출해줘야한다.
File( string n, int s ) : Item(n), size(s) {}
virtual int GetSize() { return size; }
};
class Folder : public Item
{
vector<Item*> items;
public:
Folder( string n ) : Item(n) {}
void Add( Item* p ) { items.push_back(p); }
virtual int GetSize()
{
int sz = 0;
for ( int i = 0; i < items.size(); ++i )
{
// 다형성( Polymorphism ), 가상함수를통해객체에따라다른기능을제공한다.
sz += items[i]->GetSize();
}
return sz;
}
virtual void print( string prefix ) const
{
cout << prefix << GetName() << endl; // 먼저자신의이름출력
prefix = prefix + string("\t");
for ( int i = 0; i < items.size(); ++i )
{
items[i]->print( prefix );
}
}
};
void main()
{
Folder* R = new Folder("ROOT");
Folder* A = new Folder("A");
Folder* B = new Folder("B");
R->Add(A);
R->Add(B);
R->Add( new File( "a.txt", 10 ) );
A->Add( new File( "b.txt", 20 ) );
B->Add( new File( "c.txt", 30 ) );
cout << R->GetSize() << endl;
R->print("");
}
// 의미: 자식에게 반드시 특정함수를 만들게 하는것!
// Abstract Base Class 추상기반클래스
// 강한결합 tightly compling 값에 의한 전달!
// 약한결합 loosely compling 인터페이스에 의한 결합
// 사람과 전화기 제조업자가 지켜야 하는 계약서를 먼저 만든다. ( interface, contract )
// 인터페이스 설계의 중요성 : 확장성, 변화에 유연해 진다.
// 구현 부분이 없기 때문에 메모리를 잡지 않는다.
#define interface struct //왠지 의미전달이 제대로 된다.멋있음.!!!
interface IPhone
{
virtual void Calling( char* num ) = 0;
};
class IMP3Play
{
public:
virtual void MP3Play() = 0;
};
//---------------------------------------------------------------
// 계약에 따른 전화기를 사용하는 객체
class People
{
public:
void UsePhone( IPhone* p ) { p->Calling("119"); }
};
// 모든 전화기는 IPhone 인터페이스를 구현해야(상속 받아서 순수가상함수 재정의) 한다.
class AnyCall : public IPhone
{
public:
void Calling( char* ) {}
};
void main()
{
People p;
AnyCall a;
p.UsePhone( &a );
}
#include <iostream>
#include <typeinfo.h>
// template에서instatiation 된type을알아보는방법
// const T& 는char const[6] 그러므로문자열의길이가다르다면에러!!
// T 는char const* 으로타입이결정된다.
// 그러므로타입을정할때조심해서정해야한다.!!!
template<typename T> void foo( T a, T b )
{
cout << typeid(a).name() << endl;
}
template<typename T> void goo( const T& a, const T& b )
{
cout << typeid(a).name() << endl;
}
void main()
{
foo( "hello", "hel" );
goo( "hello", "hello" );
//goo( "hello", "hel" ); // error
}
///////////////////////////////////////////////////////////////////
// RTTI : Run Time Type Information
class A
{
public:
virtual ~A() {} // 상속이라면100% 가상함수를쓰게된다.
};
void foo( B* p )
{
}
class B : public A
{
public:
};
void foo( A* p )
{
// 여기서p가A인지B인지를알고싶다면!!
// B* pp = (B*)p; // error A가넘어오게되면undefine??
// 방법1. typeid() 연산자사용
const type_info& t = typeid(*p); // 6.0에서는\GR 옵션을줘야한다.
cout << t.name() << endl; // 가상함수가있어야제대로동작한다(소멸자라도추가)
if ( typeid(*p) == typeid(B) )
{
B* pp = (B*)p; // 안전하다.
}
// 방법2. 이거만하도록나온dynamic_cast. DYNAMIC_CAST() <-- MFC용
B* p2 = dynamic_cast<B*>(p); // down cast 가발생하면0이리턴된다.
if ( p2 != 0 )
{
// p2사용
}
}
void main()
{
B b;
foo( &b );
}