2016년 7월 7일 목요일

C++ STL 수업 - 1일차

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;
}

댓글 없음:

댓글 쓰기