ICore 강민석 대표님 강의.
Generic 프로그래밍
#include <iostream>
using namespace std;
char * xstrchr(char *s, char c)
{
while (*s != 0 && *s != c)
++s;
return *s == 0 ? 0 : s;
}
int main()
{
char s[] = "abcdefghijk";
char *p = xstrchr(s, 'c');
if (p == 0)
cout << "찾을 수 없습니다." << endl;
else
cout << *p << endl;
}
#include <iostream>
using namespace std;
// 검색 구간을 일반화 - 부분 문자열 검색이 가능하게 하자
// 일반화(Generic) 프로그래밍 : 하나의 함수(클래스)를 최대한 활용 범위를 넓혀서 만들자는 개념.
char * xstrchr(char *f, char *l, char c)
{
while (f != l && *f != c)
++f;
return f == l ? 0 : f; // 끝조건은 검사하지 않는다.
}
int main()
{
char s[] = "abcdefghijk";
char *p = xstrchr(s, s+5, 'c');
if (p == 0)
cout << "찾을 수 없습니다." << endl;
else
cout << *p << endl;
}
#include <iostream>
using namespace std;
// 3 단계 : 검색 대상의 타입을 일반화 > 템플릿 도입
// 문제 1: 결과 값이 반드시 입력과 같아야 함.
// 구간의 타입과 요소의 타입이 연관 됨. dobule의 배열에서 int를 찾을 수 없음.
// 문제 2: T*라고 하면 진짜 포인터만 사용해야 함. 스마트 포인터를 사용할 수 없음.
template<typename T>
T * xfindchr(T *f, T *l, T c)
{
while (f != l && *f != c)
++f;
return f == l ? 0 : f; // 끝조건은 검사하지 않는다.
}
int main()
{
double s[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 ,10 };
double *p = xfindchr(s, s + 10, 5);
if (p == 0)
cout << "찾을 수 없습니다." << endl;
else
cout << *p << endl;
}
#include <iostream>
using namespace std;
// 4 단계 :
template<typename T1, typename T2> T1 xfindchr(T1 f, T1 l, T2 c)
{
while (f != l && *f != c)
++f;
return f == l ? 0 : f; // 끝조건은 검사하지 않는다.
}
int main()
{
double s[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 ,10 };
double *p = xfindchr(s, s + 10, 5.0);
if (p == 0)
cout << "찾을 수 없습니다." << endl;
else
cout << *p << endl;
}
STL 컨테이너
#include <iostream>
using namespace std;
// xfind는 이동과 참조를 포인터 연산 ++, *를 사용
// 반면 slist는 링크 이동과, data 필드를 이용.
// 둘 사이를 잇기 위해 ++와 *를 override하는 iterator개체 사용.
template <typename T> struct Node
{
T data;
Node * next;
Node(const T& a, Node *n) : data(a), next(n) {}
};
template <typename T> class slist_iterator
{
Node <T>* current;
public:
inline slist_iterator(Node<T>* p = 0) : current(p) {}
// 이 객체는 포인터 처럼 동작해야 함 (++ , * , ==, != 연산이 필요 함)
inline slist_iterator & operator++() {
current = current->next;
return *this;
}
inline T& operator*() {return current->data; }
inline bool operator==(const slist_iterator &it) { return current == it.current;}
inline bool operator!=(const slist_iterator &it) { return current != it.current;}
};
template <typename T> class slist
{
Node<T> * head;
public :
slist() : head(0) {}
// Node의 생성자를 잘 활용해서 slist의 앞에 넣는 코드.
void push_front(const T& a) { head = new Node<T>(a, head); }
// 모든 컨테이너 설계자는 반드시 자신의 시작과 마지막 다음(0)을 가르키는 반복자를 리턴하는 함수 2개가 있어야 한다.
slist_iterator<T> begin() { return slist_iterator<T>(head); }
slist_iterator<T> end() { return slist_iteraor<T>(0); }
};
int main()
{
slist<int> s;
s.push_front(10);
s.push_front(20);
s.push_front(30);
s.push_front(40); // 이때의 메모리 그림 page6.
slist_iterator<int> p = s.begin();
while (p != s.end()) {
cout << *p << endl;
++p;
}
}
반복자 (Iterator)
#include <iostream>
using namespace std;
// 싱글 링크드 list를 생각해 봅시다.
template <typename T> struct Node
{
T data;
Node * next;
Node(const T& a, Node *n) : data(a), next(n) {}
};
template<typename T> class slist
{
Node<T> * head;
public:
slist() : head(0) {}
// Node의 생성자를 잘 활용해서 slist의 앞에 넣는 코드.
void push_front(const T& a) { head = new Node<T>(a, head); }
};
int main()
{
slist<int> s;
s.push_front(10);
s.push_front(20);
s.push_front(30);
s.push_front(40); // 이때의 메모리 그림 page6.
}
#include <iostream>
using namespace std;
// xfind는 이동과 참조를 포인터 연산 ++, *를 사용
// 반면 slist는 링크 이동과, data 필드를 이용.
// 둘 사이를 잇기 위해 ++와 *를 override하는 iterator개체 사용.
template<typename T1, typename T2> T1 xfind(T1 f, T1 l, T2 c)
{
while (f != l && *f != c)
++f;
return f;
}
template <typename T> struct Node
{
T data;
Node * next;
Node(const T& a, Node *n) : data(a), next(n) {}
};
template <typename T> class slist_iterator
{
Node <T>* current;
public:
inline slist_iterator(Node<T>* p = 0) : current(p) {}
// 이 객체는 포인터 처럼 동작해야 함 (++ , * , ==, != 연산이 필요 함)
inline slist_iterator & operator++() {
current = current->next;
return *this;
}
inline T& operator*() { return current->data; }
inline bool operator==(const slist_iterator &it) { return current == it.current; }
inline bool operator!=(const slist_iterator &it) { return current != it.current; }
};
template <typename T> class slist
{
Node<T> * head;
public:
slist() : head(0) {}
// Node의 생성자를 잘 활용해서 slist의 앞에 넣는 코드.
void push_front(const T& a) { head = new Node<T>(a, head); }
// 모든 컨테이너 설계자는 반드시 자신의 시작과 마지막 다음(0)을 가르키는 반복자를 리턴하는 함수 2개가 있어야 한다.
typedef slist_iterator<T> iterator;
iterator begin() { return iterator(head); }
iterator end() { return iterator(0); }
};
int main()
{
slist<int> s;
s.push_front(10);
s.push_front(20);
s.push_front(30);
s.push_front(40); // 이때의 메모리 그림 page6.
slist<int>::iterator p2 = xfind(s.begin(), s.end(), 30);
cout << *p2 << endl;
slist<int>::iterator p = s.begin();
while (p != s.end()) {
cout << *p << endl;
++p;
}
}
반복자 사용
#include <iostream>
#include <list>
#include <vector>
#include <string>
#include <algorithm> // find() 같은 일반 함수가 이 헤더에 정의. 흔히 "알고리즘"이라고 함.
using namespace std;
int main()
{
string s = "hello world";
string::iterator p1 = s.begin();
++p1;
cout << *p1 << endl;
typedef list<int> Container;
Container s1 = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; //C++11문법
// s1 반복자 꺼내서 전부 출력
// s1 뒤집어 보기. reverse()
// s1 출력
Container::iterator si = s1.begin();
while (si != s1.end())
{
cout << *si << endl;
++si;
}
reverse(s1.begin(), s1.end());
si = s1.begin();
while (si != s1.end())
{
cout << *si << endl;
++si;
}
}
반복자 무효화
#include "show.h"
int main() {
vector<int> v(10, 3); // 10개를 3으로 초기화
vector<int>::iterator p = v.begin();
cout << *p << endl;
//v.resize(20); // 이 순간 반복자는 무효화 됨. 사용하면 안됨.
v.resize(5); // 크기가 줄어들때는 무효화 되지 않음.
cout << v.size() << endl;
cout << v.capacity() << endl;
cout << *p << endl;
}
반복자가 다를 수 밖에 없는 이유
#include "show.h"
#include "slist.h"
int main() {
slist<int> s;
s.push_front(10);
s.push_front(20);
slist<int>::iterator p = s.begin();
++p; // OK
--p; // 자료 구조 특성상 지원 불가능...
}
반복자 종류
#include "show.h"
#include "slist.h"
// 반복자 분류 : 반복자가 지원하는 operator에 따라 (category)
// 1. 입력 : = *p, ++ operator 지원. one pass 만 보장 함.
// 2. 출력 : *p =, ++ operator 지원. one pass 만 보장 함.
// multi pass 보장함.
// 3. 전진형 : 입출력, ++ operator 지원 ==> 싱글리스트
// 4. 양방향 : 입출력, ++ -- operator 지원==> 더블리스트
// 5. 임의접근 : 입출력, ++, --, [], +, - operator 지원 ==> 연속된 메모리와 유사 하나 항상은 아님. p = p+5 , p = p -3.
// vector, deque(일부는 연속되어 있지는 않지만, 연속된 경우는 성능 효과 있으므로, + - 연산 지원함)
// 6. c++11에서 반복자의 분류를 추가 합니다.
// continuous 반복자 : 연속된 메모리 보장할 때만 사용. (배열)
// 입력 / 출력은 프로그램의 관점에서 본다.
// 프로그램 --> container는 출력 *p =
// container--> 프로그램 입력 = *p
// stl은 최초의 interface만 제공.
// 어떤 알고리즘이 어떤 operator를 지원하는지 정확히 알고 있어야 빌드 오류가 안남.
// 또한 반복자가 제공하는 operator를 이용하여 다른 알고리즘이나 멤버 함수를 사용하여 목표를 달성할 수는 있음.
int main() {
int x[10] = { 1,2,3,4,5,6,7,8,9,10 };
}
copy 알고리즘
#include "show.h"
int main() {
int x[10] = { 1, 2,3,4,5,6,7,8,9, 10 };
vector<int> v(10, 0);
copy(x, x + 10, v.begin()); // x ==> v에 복사
show(v);
}
스트림 반복자
#include "show.h"
int main() {
ifstream fin("7_stream_iter.cpp"); // 입력 파일
ofstream fout("7_stream_iter_cped.cpp");
//istream_iterator<char> p1(fin), p2; // p2 디폴트 생성자는 EOF
istreambuf_iterator<char> p1(fin), p2; // white space 포함 해서 입력
ostream_iterator<char> p3(fout, "");
copy(p1, p2, p3);
}
int min3() {
ifstream fin("7_stream_iter.cpp"); // 입력 파일
//istream_iterator<char> p1(fin), p2; // p2 디폴트 생성자는 EOF
istreambuf_iterator<char> p1(fin), p2; // white space 포함 해서 입력
//cout << *p1 << endl;
ostream_iterator<char> p3(cout, "");
copy(p1, p2, p3);
return 0;
}
int min2() {
istream_iterator<int> p(cin); // p는 cin의 입력 버퍼를 가르키는 반복자
int n = *p;
cout << n << endl;
return 0;
}
int min() {
int x[10] = { 1, 2,3,4,5,6,7,8,9, 10 };
ostream_iterator<int> p(cout, " "); // p 는 출력 버퍼를 가르키는 반복자
*p = 10; // cout << 10 // 출력 후 자동으로 증가(++p) 되는 특징이 있음.
*p = 20;
copy(x, x + 10, p);
return 0;
}
#include "show.h"
#include <iterator>
#include <fstream>
int main() {
ifstream fin("7_stream_iter.cpp"); // 입력 파일
ofstream fout("7_stream_iter_cped.cpp");
//istream_iterator<char> p1(fin), p2; // p2 디폴트 생성자는 EOF
istreambuf_iterator<char> p1(fin), p2; // white space 포함 해서 입력
ostream_iterator<char> p3(fout, "");
copy(p1, p2, p3);
}
int min3() {
ifstream fin("7_stream_iter.cpp"); // 입력 파일
//istream_iterator<char> p1(fin), p2; // p2 디폴트 생성자는 EOF
istreambuf_iterator<char> p1(fin), p2; // white space 포함 해서 입력
//cout << *p1 << endl;
ostream_iterator<char> p3(cout, "");
copy(p1, p2, p3);
return 0;
}
int min2() {
istream_iterator<int> p(cin); // p는 cin의 입력 버퍼를 가르키는 반복자
int n = *p;
cout << n << endl;
return 0;
}
int min() {
int x[10] = { 1, 2,3,4,5,6,7,8,9, 10 };
ostream_iterator<int> p(cout, " "); // p 는 출력 버퍼를 가르키는 반복자
*p = 10; // cout << 10 // 출력 후 자동으로 증가(++p) 되는 특징이 있음.
*p = 20;
copy(x, x + 10, p);
return 0;
}
함수 탬플릿을 이용한 클래스 탬플릿 생성
#include "show.h"
// 클래스 템플릿은 암시적 추록이 되지 않으므로, 사용시 복잡해 보인다.
// 암시적 추론이 가능한 함수 템플릿을 사용해서 클래스 탬플릿을 생성한다.
#if 0
template<typename T>
back_insert_iterator<T> back_inserter(T&c)
{
return back_insert_iterator<T>(c);
}
#endif
int main()
{
int x[5] = { 1, 2,3,4,5 };
list<int> s(5, 0); // 5개를 0으로 초기화
//copy(x, x + 5, s.begin()); // 기존 5개에 대해 새로 노드를 추가가 아니라, 덮어쓰기다.
// s의 끝에 추가 하는 방업 3가지
// 1. push_back 사용
//for (int i = 0; i < 5; i++)
//s.push_back(x[i]);
// 2. 후방 삽입 반복자 사용
//back_insert_iterator<list<int>> p(s); // p는 s의 끝에 추가하는 반복자.
//*p = 30; // s.push_back(30)와 같은 의미.
//copy(x, x + 5, p);
//3. 후방 삽입 반복자 만드는 함수 사용.
//copy(x, x + 5, back_inserter(s));
show(s);
}
#if 0
<int>를 없앨 수 있나 ?
template <typename T> T square(T a) { return a*a; }
square(3); // 컴파일러가 T결정. 암시적 추론. OK
square<int>(3); // 사용자가 T결정. 명시적 추론. OK
list<int>s(5, 3); // c++에서는 클래스는 <int> 제거시 암시적 추론 불가능. 현재 지원 논의 중.
#endif
함수 버전 삽입 반복자
#include "show.h"
int main()
{
list<int> s(5, 0);
int x[5] = { 1,2,3,4,5 };
// 덮어쓰기
//copy(x, x + 5, s.begin());
// 끝에 추가하기
//copy(x, x + 5, back_inserter(s));
// 앞에 추가하기
//copy(x, x + 5, front_inserter(s)); // front_insert_iterator<> 생성
// 중간에 넣기. s의 2번째 넣기
//copy(x, x + 5, inserter(s, ++s.begin()));
// 임의 삽입 통한 앞에 넣기
copy(x, x + 5, inserter(s, s.begin()));
// 클래스 버전 : insert_iterator<T>
}
역 반복자
#include "show.h"
// reverse_iterator의 원리
#if 0
template <typename T> class reverse_iterator
{
T &p;
public:
reverse_iterator(T&t) : p(t) { {}
reverse_iterator& operator++() {
--p;
return *this;
}
reverse_iterator& operator--() {
++p;
return *this;
}
}
#endif
int main()
{
list <int> s = { 1,2,3,1,2,3, 4, 5 };
//list<int>::iterator p = s.begin();
//list<int>::reverse_iterator p = s.rbegin();
//list<int>::const_iterator p = s.cbegin();
//list<int>::const_reverse_iterator p = s.crbegin();
//*p = 20;
//++p;
//cout << *p << endl;
list<int>::iterator p = find(s.begin(), s.end(), 2);
cout << *p << endl;
++p;
cout << *p << endl;
list<int>::reverse_iterator p2= find(s.rbegin(), s.rend(), 2);
cout << *p2 << endl;
++p2;
cout << *p2 << endl;
}
함수
#include "show.h"
/*
알고리즘에 인자로 함수가 전달되는 경우가 많음
단항 함수 : 인자가 한개 인 것
이항 함수 : 인자가 2개 인 것
*/
bool foo(int a) { return (a % 4) == 0; }
int main()
{
list<int> s = { 1,2,3,8,7,6, 12 };
//1. s에서 처음 나오는 3을 찾아 보기
//list<int>::iterator p1 = find(s.begin(), s.end(), 3); // 상수 검색 버전
//cout << *p1 << endl;
//2. s에서 처음 나오는 4의 배수를 찾기
list<int>::iterator p2 = find_if(s.begin(), s.end(), foo);
//s.begin()에서 s.end()까지 foo에게 보내서 foo를 참으로 되는 경우를 p2에 보냄
cout << *p2 << endl;
}
함수 객체
#include "show.h"
/*
함수 객체 : 괄호() 연산자를 재정의해서 함수처럼 사용하는 것을 함수객체라고 함.
장점
1. 객체이므로 상태를 가질 수 있다. 일반 함수는 상태가 없다.
상태와 생성자를 잘 활용하면 재사용성이 좋아진다.
2. 인라인 치환된다.
find_if(s.begin(), s.end(), foo); // foo함수가 다시 호출될 때, 함수 포인터로 전달 한 경우이므로 inline 치환 안됨. 느리다.
module m(4);
find_if(s.begin(), s.end(), m); // m함수 객체가 다시 호출될 때 inline 치환됨. 더 빠름.
*/
struct modulus {
int value;
modulus(int n) : value(n) {}
bool operator()(int a) { return a%value == 0; }
};
int main()
{
modulus m(3); // 생성자 호출 value를 3으로 초기화.
bool b = m(4); // 생성자가 아니라 m.operator()(4)로 실행됨.
cout << b << endl;
/*
a+b; // a.operator+(b);
a-b; // a.operator-(b);
a(); // a.operator()(); a.operator()에서 ()연산자 이름. a.operator()()의 두번째 ()는 함수 호출.
a(1); // a.operator()(1);
*/
}
함수 객체의 장점
(1) 인라인 치환 되어 성능 빠름 (2) 내부 상태를 가질수 있음
#include "show.h"
int foo(int a, int b) {
cout << "foo" << endl;
return a + b;
}
struct Plus{
inline int operator()(int a, int b) { return a + b; }
};
template <typename T>
struct PlusT {
inline T operator()(T a, T b) { return a + b; }
};
int main()
{
int x[5] = { 1,2,3,4,5 };
int y[5] = { 1,2,3,4,5 };
vector<int> v(5, 0);
// 일반 함수를 알고리즘 인자로 보내면 인라인 치환 안된다. 느리다.
//transform(x, x + 5, y, v.begin(), foo);
//함수 객체를 만들어 넘기면 인라인 치환된다. 빠르다.
//PlusT<int> p;
//transform(x, x + 5, y, v.begin, p);
plus<int> p2;
transform(x, x + 5, y, v.begin(), p2);
show(v);
}
알고리즘에 조건 전달 방법 3가지
#include "show.h"
int foo(int a, int b) {
cout << "foo" << endl;
return a + b;
}
int main() {
int x[5] = { 1,2,3,4,5 };
int y[5] = { 1,2,3,4,5 };
vector<int> v(5, 0);
// 1. 일반함수
// transform(x, x+5, y, v.begin(), foo);
// 2. 함수객체
//transform(x, x + 5, y, v.begin(), plus<int>()); // 클래스 이름() : 임시객체 생성. 문자의 끝(;)에서 파괴 됨.
// 3. C++11의 람다 표현식 문법 ==> 일반함수와 달리 inline 치환됨. g++은 4.9.x vc++ 2013이상.
transform(x, x + 5, y, v.begin(), [](int a, int b) {return a + b;}); // [] 람다 인트로듀서.
show(v);
}
바인더
#include "show.h"
int main()
{
modulus<int> m; // %연산. 2항 함수 개체.
int n = m(10, 3); // 10%3을 함. --> 1
cout << n << endl;
int x[10] = { 1,2,3,4,5,6,7,8,9,10 };
// 3의 배수를 찾고 싶다.
//int *p = find_if(x, x + 10, m ); // ??? find_if의 세번째 인자는 1항 함수여야 한다. 반면 m는 입력으로 2항을 필요로 함. //error
// 이항 함수 객체를 단항 함수 객체로 변경하는 도구.
binder2nd<modulus<int>> f(m, 3); // m의 2번째 인자를 3으로 고정한 단항함수 f
cout << f(10) << endl;
int *p = find_if(x, x + 10, f);
cout << *p << endl;
// binder헬퍼 : binder2nd<>를 만들어 주는 헬퍼 함수
int *p = find_if(x, x + 10, bind2nd(m,3)); // bind2nd(m,3) : m의 2nd 인자를 3으로 고정한 걸 쓰겠다. // inline 함수라 빠르다.
}
#include "show.h"
using namespace std::placeholders; //_1, _2, _3
void foo(int a, int b, int c, int d)
{
printf("%d %d %d %d\n", a, b, c, d);
}
int main()
{
modulus<int> m;
cout << m(10, 3) << endl;
// bind1st(), bind2nd() : 이항함수 객체를 단항 함수객체로... c++11에서 deprecated.
// 이항 함수 객체에 대해서만 가능. 3항 이상에서는 안됨.
cout << bind2nd(m, 3)(10) << endl; // 10 % 3
cout << bind1st(m, 3)(2) << endl; // 3 % 2 // m의 1st 인자를 3으로 고정.
// c++11에서는 3항이상 및 함수 객체 뿐만 아니라 함수도 bind 지원한다.
// M항을 N항으로 고정할 수 있다.
cout << bind(m, _1, 3)(10) << endl; // m(10,5) 10%5
cout << bind(m, 10, _1)(3) << endl; // m(10, 3) 10%3
cout << bind(m, 10, 3)() << endl; // m(10, 3) 10%3
bind(&foo, 3, _2, 2, _1)(9, 7); // foo는 함수객체가 아닌 함수. 3, 7, 2, 9
return 0;
}
STL 함수 포인터 객체
#include "show.h"
using namespace std::placeholders; //_1, _2, _3
void foo(int a, int b, int c, int d) { printf("%d %d %d %d\n", a, b, c, d); }
void goo(int a, int b) { cout << "goo" << endl; }
void hoo(int a) { cout << "hoo" << endl; }
int main()
{
void (*f1)(int) = &hoo;
//f1 = &goo; // 인자 개수 signature가 달라서 error
// STL 의 범용적 함수 포인터 객체
function<void(int)> f = &hoo; // 함수
f(10); // hoo(10);
f = bind(&goo, _1, 7);
f(10); // goo(10,7);
f = bind(&foo, 2, 8, _1, 1);
f(3); // 2, 8, 3, 1
return 0;
}
함수와, 함수의 포인터 사용 차이.
class Dialog{
public :
void Close() {}
};
void foo(){}
int main()
{
int a; // a는 int. &a는 int *이다.
void (*f1)() = &foo; // OK
void (*f2)() = foo; // OK. 하지만, "함수가 함수 주소로 암시적 형변환 되어서 대입되고 있음." 예외적 문법. C언어의 특징.
void(Dialog::*f3)() = &Dialog::close; // OK
void(Dialog::*f3)() = Dialog::close; // NG. 멤버 함수 이름은 함수 포인터로 암시적 형변환이 안됨. 반드시 주소를 붙여야 함. C++언어의 특징.
}
배열의 타입
#include "show.h"
int main()
{
int n = 10; // n의 데이터 타입은 int.
double d = 3.4; // d의 데이터 타입은 double.
int* p = &n; // 주소를 꺼내려면 &사용.
// 포인터 변수를 만들려면 변수 이름앞에 * 표시.
int x[5] = { 1, 2, 3, 4, 5 }; // x의 타입은 int[5].
//변수의 선언에서 이름을 빼면 그 변수의 타입이다.
// x의 주소를 꺼내서 p2에 담아 보세요.
int *p2 = x; // 배열의 주소가 아님. 배열의 이름은 배열의 1번째 요소의 주소로 암시적 형변환 됩니다.
// c++ 표준 문서 4-2. Array to Pointer Conversion 문법.
int(*p3)[5] = &x; // 이 정확한 배열의 주소 표현. 즉 선언에서 이름을 제거하고 포인터로 바꾸면 됨. []이 *보다 순위가 높아서 ()로 묶어 줄 필요 있음.
printf("%p %p\n", p2, p2 + 1); // 몇 바이트 차이 나는지 확인
printf("%p %p\n", p3, p3 + 1);
int y[2][2] = { {1,1}, {2, 2} };
int(*p4)[2][2] = &y; // OK
int(*p5)[2] = y;
}
객체의 초기화
#include "show.h"
int main()
{
int n1 = 10;
int n2 = n1;
// 모든 객체는 자신과 동일한 타입으로 초기화 가능 하다.
//단, 자신으로 타입으로 초기화 할 수 없는 것은(1) 배열(2) 함수
int &r = n1;
int x[5] = { 1, 2, 3,4,5 };
//int y[5] = x; // NG :
//대신 1번째 요소의 주소로 형변환 될 수 있다.
//모든 변수는 참조 변수를 만들며 초기화 될 수있다.
int *p = x; //OK
int(&r2)[5] = x; // OK
}
배열의 타입
#include "show.h"
template<typename T> void foo(T a)
{
cout << typeid(T).name << endl;
}
template<typename T> void goo(T& a)
{
cout << typeid(T).name << endl;
}
int main()
{
int n = 10;
double d = 3.4;
int x[5] = { 1, 2, 3, 4, 5 };
foo(n); // T == int
foo(d); // T == double
foo(x); // T ?? int a[5] = x; ==> 복사 문제 발생. 대신 int *a = x가 된다. 즉, T는 int*
goo(x); // T ?? int a[5] = x;는 안됨. 단, int(&a)[5] = x;는 가능.
}
배열의 타입 2
template<typename T> void foo(T a, T b)
{
}
template<typename T> void goo(T & a, T & b)
{
}
int main()
{
foo("apple", "banana"); // 문자열은 원래 정체가 배열. 다만, 함수 호출 시 포인터로 형변환 됨.
goo("apple", "banana"); // 참조로 보내면, 진짜 배열이 넘어감. char[6], char[7] 2개의 다른 타입이 넘어감. 그래서 ERROR.
goo("apple", "banna"); // 참조로 보내면서 둘다 char[6], char[6]여서 이건 ERROR가 안남.
}
show.h
#pragma once
#include <iostream>
#include <list>
#include <vector>
#include <string>
#include <algorithm>
#include <iterator>
#include <fstream>
#include <functional>
using namespace std;
template<typename T> void show(T& c)
{
typename T::iterator p = c.begin();
while (p != c.end()) {
cout << *p << " ";
++p;
}
cout << endl;
}
댓글 없음:
댓글 쓰기