2016년 7월 7일 목요일

C++ STL 수업 - 3일차


스마트 포인터

#include "show.h"
#include <memory>
/*
스마트 포인터
C/C++에서는 new는 한곳이지만, delete는 여러 곳에 존재.
아이디어 : 포인터는 블럭 벗어나도 사라지지 않지만,
           [객체]는 블럭 벗어날 때 자동 delete된다.

실제는 [객체]이지만, *, ++, --, -> 등 override해서 지원. 마치 포인터 처럼 사용.
3가지 : shared_ptr, weak_ptr, unique_ptr
*/

class Car
{
public:
    void Go() { cout << "Car Go" << endl; }
    ~Car()    { cout << "Car 파괴" << endl; }
};

#if 0
int main()
{
    Car *p = new Car;
    delete p;
}
#endif


int main()
{
    //    shared_ptr<Car> p = new Car;  // ERROR. explicit 생성자는 =로 할당 불가능
    shared_ptr<Car> p(new Car);   // OK. 생성자가 explicit 생성자 이기 때문이다.
                                  // shared_ptr<Car>는 객체다. 포인터는 아니다.
    p->Go(); // 그럼에도 p에 ->를 붙여 포인터처럼 쓰는 것은 -> operator를 재정의 한것이다.
             // 스콥이 나가면 Car는 destroy 된다. 때문에 delete 불필요. 게임 업체에서는 진짜 포인터 안씀.
    return 0;
}



#include "show.h"
#include <memory>
/*
스마트 포인터
C/C++에서는 new는 한곳이지만, delete는 여러 곳에 존재.
아이디어 : 포인터는 블럭 벗어나도 사라지지 않지만,
[객체]는 블럭 벗어날 때 자동 delete된다.

*/

class Car
{
public:
    void Go() { cout << "Car Go" << endl; }
    ~Car() { cout << "Car 파괴" << endl; }
};

/* p1 p2도 둘다 같은 object를 가리키게 됨.
참조 개수를 관리 한다. 모든 스마트 포인터가 같은 참조 개수를 공유 한다.

1
2
1
A
Car 파괴
*/
int main()
{
    shared_ptr<Car> p1(new Car);

    cout << p1.use_count() << endl;
    {
        shared_ptr<Car> p2 = p1;
        cout << p1.use_count() << endl;
    }
    cout << p1.use_count() << endl;

    cout << "A" << endl;
    return 0;

} // ~Car() 불림.




#include "show.h"
#include <memory>
/*
스마트 포인터
C/C++에서는 new는 한곳이지만, delete는 여러 곳에 존재.
아이디어 : 포인터는 블럭 벗어나도 사라지지 않지만,
[객체]는 블럭 벗어날 때 자동 delete된다.

*/

class Car
{
    Car(int c){}
    int color;
public:
    void Go() { cout << "Car Go" << endl; }
    ~Car() { cout << "Car 파괴" << endl; }
};

void* operator new(size_t sz)
{
    cout << "new : " << sz << endl;
    return malloc(sz);
}

void operator delete(void*p)
{
    free(p);
}

/*

int main()
{
    // 아래 코드가 실행되면 메모리 할당이 몇번 일어 날까요 ?
    shared_ptr<Car> p1(new Car);
   
    1. HEAP  : new Car에 의해서 한번 발생 (new 1회)
    2. STACK : p1은 stack에 생김.
    3. HEAP  : [참조 개수] + 알파를 관리하기 위한 관리 객체가 따로 만들어짐. (new 1회)
    > 1과 3 각각 1회씩 2회의 new가 발생함.
    > 3때매 파편화 문제 발생.
    > 대응 방법으로, 1에 붙여서 3을 할당 받는 방법 생각 가능.
   

}
*/

int main()
{
    shared_ptr<Car> p1(new Car);
    // new : 4  원본객체
    // new : 16 참조개수관리객체
   
    // sizeof(Car) + sizeof(참조개수관리객체) 크기를 한번에 메모리 할당.
    // shared_ptr 쓴다면 반드시 make_shared 써야 한다 !
    shared_ptr<Car> p2 = make_shared<Car>(10);
    // 이번에는 new가 전체 1회만 발생 함.
    // new : 16 원본객체+참조개수관리객체
   
    /*
    make_shared<>로 (1) 원본객체(4바이트)와 (2) 참조개수관리객체(16바이트)를 합쳤음에도 20바이트가 아닌 16바이트인 것은
    원래 참조개수관리객체가 별도로 존재할 때 크기 16바이트 중 4바이트는 원본객체에 대한 포인터임.
    그런데 참조개수관리객체가 원본객체 끝에 달라붙으면서 그 4바이트가 필요 없게 됨.
    그래서 참조개수관리의 크기는 12바이트가 되고, 원본객체의 크기(int color 4바이트)를 더해서 16바이트가 됨.
    */
}




#include "show.h"
#include <memory>
#include <type_traits> //remove_pointer

#include <windows.h>
void foo(int *p)
{
    cout << "foo" << endl;
    delete[] p;
}

struct ArrayDeleter
{
    void operator()(int *p) { delete[] p; }
};

int main()
{
    //shared_ptr<포인터 뺀타입> p(포인터 타입);
    // 삭제자의 전달.
    shared_ptr<int> p1(new int); // new는 delete로 삭제
    shared_ptr<int> p2(new int[10], foo); // new[]는 delete[]로 삭제해야 함. 때문에 삭제자를 전달해서 삭제 가능.
    shared_ptr<int> p3(new int[10], ArrayDeleter()); // 임시객체로 전달
    shared_ptr<int> p4(new int[10], [](int *p) {delete[] p;}); // 람다로 전달 C++11

    shared_ptr<FILE> p5(fopen("a.txt", "wt"), fclose); // 삭제자를 fclose로 제공. 인자가 1개 이상이면, bind를 사용할 수 있다.

    //shared_ptr<포인터 뺀타입> p(포인터 타입); 이어야 하는데,
    // 아래와 같이 HANDLE도 포인터 타입일 때 처리 방법.
    HANDLE h = CreateEvent(0, 0, 0, 0);
    CloseHandle(h);
    shared_ptr<remove_pointer<HANDLE>::type> h2(CreateEvent(0, 0, 0, 0), CloseHandle);

    // 현재는 삭제자를 쓸 때는 make_shared<>를 못 씀 !
    // make_shared<> 사용시 삭제자 변경 불가능. 이후 표준에서 논의 중. 표준 위원회에 요구 사항 많음.
#if 0
    int *p1 = new int;
    delete p1;

    int *p2 = new int[10];
    delete p2;
    // C++ 표준에는 new -> delete, new [] -> delete[]만 써 있음. 그외에는 정의 안되어 있음. 그것은 컴파일러 따라 다름. "undefined" 항목.
    // 때문에 new [] -> delete는 쓰면 안됨.
#endif   

}



#include "show.h"
#include <memory>

// 중요 예제...
// 참조계수 스마트 포인터가 상호 참조가 발생하면 메모리 누수 발생 함.
struct Node {
    int data;
    // Node* next;
    // 스마트 포인터를 쓰기로 했으면, 일반 포인터와 스마트를 섞는것보다, 모두 스마트로 하는게 좋다.
    shared_ptr<Node> next;

    ~Node() { cout << "Node파괴" << endl; }
};

/*
  스마트 포인터를 쓰기로 했으면, 일반 포인터와 스마트를 섞는것보다, 모두 스마트로 하는게 좋다.

*/
#if 0
int main()
{
    shared_ptr<Node> p1(new Node);
    shared_ptr<Node> p2(new Node);

    // 스마트 포인터를 써도, 아래 경우 Node가 파괴가 안됨.
    // [상호 참조]가 일어나면 메모리 해지가 안되어 [메모리 누수] 발생 함.
    p1->next = p2;
    p2->next = p1;
}
#endif


struct Node2 {
    int data;
    // Node* next;
    // 스마트 포인터를 쓰기로 했으면, 일반 포인터와 스마트를 섞는것보다, 모두 스마트로 하는게 좋다.
    weak_ptr<Node2> next;

    ~Node2() { cout << "Node2파괴" << endl; }
};

// 해결책으로, 참조개수가 증가되지 않는 스마트 포인터가 필요. weak_ptr<>
// s로 시작 하는 스마트 포인터는 모두 참조 계수 개체
// w로 시작 하는 스마트 포인터는 weak 참조
int main()
{
    shared_ptr<Node2> p1(new Node2);
    shared_ptr<Node2> p2(new Node2);

    p1->next = p2;
    p2->next = p1;
}


일반 포인터가 아닌 weak_ptr<>을 써야 하는 이유 
#include "show.h"
#include <memory>

int main()
{
    int *p = 0;

    weak_ptr<int> wp;
    {
        shared_ptr<int> sp(new int);

        cout << sp.use_count() << endl; // 1
       
        p = sp.get();
        wp = sp;

        cout << sp.use_count() << endl; // 1
    } // shared_ptr<int> sp(new int); 자원 파괴 됨.

    cout << p << endl;
    // 이 경우 [자원]은 파괴 되었지만, 일반포인터 p가 [주소]는 계속 가지고 있음.
    // p는 [자원]이 진짜 살아 있는지 아닌지 알 수 있는 방법이 없음.

    // 해결책:
    // wp는 자원뿐만 아니라, 참조개수관리객체도 가리키게 됨.
    // wp가 하나라도 살아 있으면 [자원 객체]는 파괴되더라도 [참조개수관리객체]는 살아 있게 됨.
    // weak_ptr<>를 사용시 자원이 파괴 되었는지 알 수 있다.

    cout << wp.use_count() << endl; // 0 : [자원]이 파괴 되었음을 알 수 있음.
    // make_shared여도 역시 가능.

    // 진짜 중요한 것은 아래와 같이 쓸 수 있느냐? 절대 안됨. 멀티 쓰레드 환경에서는 문제가 됨.
#if 0
    if (wp.use_count() > 0) { // 여기가 true이다가 다른 쓰레드에 의해서 파괴되는 경우.
        // 자원이 살아 있다고 판단하고 사용 하는 경우
        wp->멤버 호출(); // 이 시점에는 false 일 수 있다.
    }
#endif
    // 때문에 wp는 ->연산자를 지원하지 않는다.
    // 즉, 자원 접근을 허용하지 않는다 !!!

    // weak_ptr<>로 shared_ptr<>을 다시 [생성]해야 합니다 !!
    shared_ptr<int> sp2 = wp.lock();
    // 여기 실행되기 전에 죽으면 상관 없으나, [자원]이 있었으면 다른 thread에 의해서 파괴될 수 없다.
    // 기 자원이 파괴된 경우는 sp2는 0;
    // lock이 참조개수의 thread safe을 보장 한다. wp.lock()은 shared_ptr<>을 하나 생성해서 넘겨 준다.
   
    // Android는 wp.promote();
    if (sp2 == 0)
        cout << "자원 파괴 됨" << endl;
    else
        cout << "자원 사용 가능" << endl;

}


쓰레드와 스마트 포인터가 섞여 있을 때 문제 점
#include "show.h"
#include <memory>
#include <windows.h>

// cafe.naver.com/cppmaster 예전 수업 자료실에서 "키캣" 검색해서 받아 보기
// core/core/libutils/thread.cpp

// 3번째 파라미터를 thread로 실행 함.
class Thread
{
public:
    void run() { CreateThread(0, 0, foo, this, 0, 0); }
    static DWORD __stdcall foo(void*p) {
        Thread* self = (Thread*)p;
        self->threadMain();
        return 0;
    }

    virtual void threadMain(){}
};

class MyThread : public Thread
{
    int data;
public:
    virtual void threadMain() { data = 10; cout << "my thread" << endl; }
};

int main()
{
    {
        shared_ptr<MyThread> p(new MyThread);
        p->run();
    }
    // 이 블럭을 벗어나는 순가 p는 destory 됨. 반면 thread는 동작 중.
    // 쓰레드 객체의 수명을 쓰레드가 죽을 때까지 이어야 한다.
    // 그 방법?

    while (1);
}


#include "show.h"
#include <memory>
#include <windows.h>

// 쓰레드와 스마트 포인터가 섞여 있을 때 문제 점

// cafe.naver.com/cppmaster 예전 수업 자료실에서 "키캣" 검색해서 받아 보기
// core/core/libutils/thread.cpp

// 그럼 언제 shared_ptr<>을 증가시켜줄 것인가가 문제.
class Thread
{
    shared_ptr<Thread> mHoldSelf;// 자신의 참조 계수를 증가하기 위해.

public:
//    Thread() : mHoldSelf(this) {} // 방1. 생성자에서 해도 되는가 ? 안됨. thread 안만들면 메모리 leak이 발생. 때문에 thread 만들 때 해야 함.

    void run(shared_ptr<Thread> sp) {
        //mHoldSelf = this; // 방2. 이렇게 해도 될까 ?
        mHoldSelf = sp; // 방3.
        cout << ">"<<mHoldSelf.use_count() << endl;
        CreateThread(0, 0, foo, this, 0, 0);
    }
    static DWORD __stdcall foo(void*p) {
        Thread* self = (Thread*)p;
        self->threadMain();

        // 참조 계수를 줄여 준다. mHoldSelf은 더이상 [자원] 참조하지 않는다. 참조계수를 무조건 0으로 만드는 것은 아님
        self->mHoldSelf.reset();
        return 0;
    }

    virtual void threadMain() {}
};

class MyThread : public Thread
{
    int data;
public:
    virtual void threadMain() { data = 10; cout << "my thread" << endl; }
    ~MyThread() { cout << "~MyThread" << endl; }
};

int main()
{
    {
        shared_ptr<MyThread> p(new MyThread);
        p->run(p);
    }
    // 이 블럭을 벗어나는 순가 p는 destory 됨. 반면 thread는 동작 중.
    // 쓰레드 객체의 수명을 쓰레드가 죽을 때까지 이어야 한다.
    // 그 방법?

    while (1);
}

/*
방2 안되는 이유.
shared_ptr<int> p1(new int);
shared_ptr<int> p2 = p1; // 참조 계수 2

int *p = new int;
shared_ptr<int> p3(p); // 계수 1
shared_ptr<int> p4(p); // 계수 1
*/



#include "show.h"
#include <memory>
#include <windows.h>
// 쓰레드와 스마트 포인터가 섞여 있을 때 문제 점

// 외부에서 shared_ptr<>로 [객체]를 관리할 때
// [객체] 스스로가 자신의 참조개수를 증가하고 싶다면
// enable_shared_from_this<> 사용 한다.

class Thread : public enable_shared_from_this<Thread> //this 로 부터 shared_ptr의 참조 개수를 증가하게 해달라
{
    shared_ptr<Thread> mHoldSelf;// 자신의 참조 계수를 증가하기 위해.

public:

    void run() {
        mHoldSelf = shared_from_this(); // 외부에서 만든 shared_ptr<>이 사용하는 관리 객체를 공유하게 한다.
        cout << ">" << mHoldSelf.use_count() << endl;
        CreateThread(0, 0, foo, this, 0, 0);
    }

    static DWORD __stdcall foo(void*p) {
        Thread* self = (Thread*)p;

        // 방2. mHoldSelf를 [지역변수]에 옮겨 담는다. reset 이후에 실수로 코드 불러서 문제 생기는 것 방지 할 수 있음 !
        shared_ptr<Thread> me(self->mHoldSelf);
        self->mHoldSelf.reset();

        self->threadMain();
        // 방1. self->mHoldSelf.reset(); 이것 대신
          // 방1 사용시 cout << data << end; // 문제 발생
        return 0;
    }

    virtual void threadMain() {}
};

class MyThread : public Thread
{
    int data;
public:
    virtual void threadMain() { data = 10; cout << "my thread" << endl; }
    ~MyThread() { cout << "~MyThread" << endl; }
};

int main()
{
    {
        shared_ptr<MyThread> p(new MyThread);
        p->run();
    }
    while (1);
}



#include "show.h"
#include <memory>

/*
관리 객체
*/
int main()
{
    shared_ptr<int> p1(new int); // 자원 + 관리객체 생성
    shared_ptr<int> p2 = p1;


    unique_ptr<int> up1(new int); // 자원만 생성. 관리 객체가 없다. 소멸자에서 delete 기능만 제공.
    //unique_ptr<int> up2 = up1; // unique_ptr<>은 복사 생성자를 지워버림. compile error 발생.

    unique_ptr<int> up2 = move(up1); // move은 원본 reset. unique_ptr은 복사는 안되지만 move는 가능 함.

    *up2 = 10;

    cout << *up2 << " sz : "  << sizeof(up2) << endl;

    *up1 = 10; // runtime error - up1은 자원이 up2에 전달 됨. unique_ptr은 int* 사용 대비 성능 저하가 없음.
    // 컴파일 시 모두 inline으로 교체 됨.

}




#include "show.h"
#include <memory>

struct Freer
{
    inline void operator()(void*p)
    {
        cout << "Freer" << endl;
        free(p);
    }
};

// 삭제자 전달 방법
int main()
{
    // 삭제자 전달  : 템플릿 인자로 전다
    // 장점 : 삭제자를 별도로 보관할 필요가 없고, 인라인 치환된다.
    // 단점 : 삭제자가 다르면 다른 타입이 된다. 같은 컨테이너에 보관이 안된다.
    unique_ptr<int> p1(new int);
    unique_ptr<int, Freer> p2((int*)malloc(100)); // unique_ptr은 관리 객체가 없음. 삭제자 지정 방법 필요 ==> template인자로 전달.
    // 탬플릿 인자가 다르면 다른 타입이다.
   
    // shared_ptr<int> p3((int*)malloc(100), foo); // 관리 객체에는 삭제자 정보도 보관.
    // shared_ptr은 왜 다르게 쓰나 ? 삭제자가 다르면 다른 타입이 되기 때문에 함께 vector같은 자료 구조로 묶을 수 없다.
    // shared_ptr<int> p3((int*)malloc(100), foo);

    unique_ptr<int[]> p3(new int[10]); // new T[] 형태면 템플릿 파라미터에 T[]를 넘겨 준다.
}



#include "show.h"
#include <memory>

int main()
{
    shared_ptr<int> sp1(new int);
    unique_ptr<int> up1(new int);

    // 다음 중 에러를 모두 골라 보세요
    shared_ptr<int> sp2 = up1; // ERROR unique_ptr 자원을 공유 하게 되기 때문에 안됨.
    unique_ptr<int> up2 = sp1; // ERROR 나 말고도 다른 놈도 있기 때문에 안됨.

    shared_ptr<int> sp3 = move(up1); // OK : move로 독점권을 포기하는 것이기 때문에 안됨.
    unique_ptr<int> up2 = move(sp1); // ERROR 나 뿐만 아니라, 다른 놈도 가르키고 있을 수 있기 때문에 안됨.
}


쓰레드

#include "show.h"
#include <chrono>
using namespace chrono;


// c++11부터 STL에서 스레드 지원 함.
#include <thread>

void foo(int a, double d)
{
    cout << "foo" << endl;
    this_thread::sleep_for(1min);
}

class FOO {
public:
    void operator()() {
        cout << "FOO" << endl;
    }
    void goo() {
        cout << "goo" << endl;
    }
};

int main()
{
    //thread t(bind(&foo, 1, 3.4)); // 스레드 바로 실행 됨.
    //thread t(&foo, 1, 3.4); // 스레드 바로 실행 됨. c++11 가변인자 템플릿으로 인해 가능 함.
   
   
    // thread t(FOO()); // ERROR 임시 객체 못 받음.
   
    FOO f;
    //thread t(f); // ERROR 임시 객체 못 받음.

    thread t(bind(&FOO::goo, &f)); // bind가 인자 고정뿐만 아니라, 객체 고정도 가능하다.
    t.join(); // 자식 스레드가 죽을 때까지 기다려 줌. 안기다리면 crash.
}


#include "show.h"
#include <chrono>
using namespace chrono;

#include <thread>
#include <mutex>
mutex m;
long x = 10;

void foo()
{
    m.lock();
    x = 100; // 여기서 exception이 난다면 ?  C++에서는 mutex중 exception나면 deadlock
    m.unlock();
}

void foo2()
{
    lock_guard<mutex> lg(m); // lock_guard 생성자에서 m.lock하고 소멸자에서 unlock.
    x = 100;
}
// C++에서는 코드 플로우 중 exception 발생하면, 로컬 변수는 destroy 한다.
// 즉, try{ } 안에 선언된 lock_guard는 catch에 도달할 때 destroy 됨이 보장 된다.
// Android Framework에서는 lock_guard에 상당하는 Autolock 클래스 지원.

int main()
{
    thread t1(&foo2);
    thread t2(&foo2);

    t1.join();
    t2.join();
}


begin
#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

#if 0
// 일반 함수 begin
template<typename T> auto begin(T&c) { return c.begin(); }
template<typename T> auto end(T&c) { return c.end(); }

template<typename T, int N> auto begin(T (&c)[N]) { return c; } // T (&c)[N] 배열 의미
template<typename T, int N> auto end(T (&c)[N]) { return c+N; }
#endif

// c.begin() 대신 begin(c)를 쓰라. 그러면 포인터도 처리 가능. C++11부터 지원
template<typename T> void show(T& c)
{
    auto p = begin(c);
    while (p != end(c)) {
        cout << *p << " ";
        ++p;
    }
    cout << endl;
}

int main()
{
    vector<int> v = { 1,2,3,4,5 };
    show(v);

    int x[5] = { 1,2,3,4,5 };
    show(x); // ??
}

//기타 : c++11은 call by value로 가자라고 함...


해시
#include "show.h"
#include <set> // RB [tree]
#include <unordered_set> // [hash] Table

int main()
{
    hash<int> hi; // 해쉬 함수(함수 객체로 되어 있음)
    cout << hi(10) << endl;
    cout << hi(20) << endl;
    cout << hi(10) << endl;

    hash<string> hs;
    cout << hs("hello") << endl;
    cout << hs("whatup") << endl;
    cout << hs("hello") << endl;

    cout << hash<double>()(3.4) << endl;
}

#include "show.h"
#include <set> // RB [tree]
#include <unordered_set> // [hash] Table

int main()
{
    unordered_set<int> s;

    s.insert(10);
    s.insert(30);
    s.insert(15);
    s.insert(20);

    pair<unordered_set<int>::iterator, bool> ret = s.insert(20); // 중복 허용하지 않음. Fail
    if (ret.second == false)
        cout << "실패" << endl;

    // s에서 30을 찾아서 출력
    unordered_set<int>::iterator p = s.find(30);
    cout << *p << endl;


}


#include "show.h"
#include <set> // RB [tree]
#include <unordered_set> // [hash] Table

struct People
{
    string name;
    int age;

    People(string name = "", int a = 0) : name(n), age(a) {}

};

int main()
{
    set<People> s1;
    s1.insert(People("kim", 10)); // set은 크기 비교 tree다. 거기에 People을 추가하려면, 비교 방법이 필요 함.
                                   // 방법1. People에 < 연산자가 필요하다.
                                   // 방법2. set<People, PeopleComparee> 등으로 비교함수 객체가 있으면 된다.
                                   // 순위는 방법2번이 우선 순위가 높다. 2가 있으면 2로 대체 된다.

    //unordered_set<People> s2;
}


#include "show.h"
#include <set> // RB [tree]
#include <unordered_set> // [hash] Table

struct People
{
    string name;
    int age;
    People(string n = "", int a = 0) : name(n), age(a) {}
};

// 사용자정의 타입을 넣으려면 2가지 필요 함.
// 1. 해쉬 함수 (함수객체)로 만들어야한다.
// 2. 상등 비교 함수객체가 있어야 한다.

struct PeopleHash
{
    inline size_t operator()(const People&p) const
    {
        return hash<string>()(p.name) + hash<int>()(p.age);
    }
};

struct PeopleEqual {
    inline bool operator() (const People&p1, const People& p2) const
    {
        return p1.name == p2.name && p1.age == p2.age;
    }
};

int main()
{
    unordered_set<People, PeopleHash, PeopleEqual> s1;
    s1.insert(People("kim", 10)); // 되게 하려면 ??
    s1.insert(People("m", 30));
    s1.insert(People("park", 40));

}



#include "show.h"
#include <set> // RB [tree]
#include <unordered_set> // [hash] Table

struct People
{
    string name;
    int age;
   
    People(string n = "", int a = 0) : name(n), age(a) {}
};

#if 0
template<typename T> class hash {}; //primary template
template<> class hash<int> { // 전문화
    inline size_t operator()(int a) { }
};
template<> class hash<string> { // 전문화
    inline size_t operator()(string a) { }
};
#endif

// C++ 표준의 hash primary template을 People버전으로 [전문화](specialization)
template<> class hash<People> { // 전문화
public:
    inline size_t operator()(const People&p) const
    {
        return hash<string>()(p.name) + hash<int>()(p.age);
    }
};

template<> class equal_to<People>
{
public:
    inline bool operator() (const People&p1, const People& p2) const
    {
        return p1.name == p2.name && p1.age == p2.age;
    }
};

// 사용자정의 타입을 넣으려면 2가지 필요 함.
// 1. 해쉬 함수 (함수객체)로 만들어야한다.
// 2. 상등 비교 함수객체가 있어야 한다.

struct PeopleHash
{
    inline size_t operator()(const People&p) const
    {
        return hash<string>()(p.name) + hash<int>()(p.age);
    }
};

struct PeopleEqual {
    inline bool operator() (const People&p1, const People& p2) const
    {
        return p1.name == p2.name && p1.age == p2.age;
    }
};

int main()
{
    unordered_set<People, PeopleHash, PeopleEqual> s1;
    s1.insert(People("kim", 10)); // 되게 하려면 ??
    s1.insert(People("m", 30));
    s1.insert(People("park", 40));

    // 만약에
    unordered_set<People> s2;
}

/*
중요 !!! 기본 개념.
template<typename T> class stack{}; // 임의의 타입에 대해 클래스 찍어 냄. (1) primary template
template<typename T> class stack<T*>{}; // 모든 타입은 1번. 포인터일때는 이것 사용. (2) 부분 전문화(특수화, 특화). partial specialization
                                        // 더블 포인터도 여기 탄다.
template<> class stack<int>{}; // int일때는 이것 사용. (3) 전문화(특수화, 특화) 버전 specialization

stack<double> s1;
stack<int*> s2;
stack<int> s2;

*/







알고리즘

#include "show.h"
// 100여개 알고리즘.

// 핵심 1: remove() 등의 알고리즘은 컨테이너 자체의 크기를 줄이지는 않음. 삭제가 아님. 리턴 값만 알려줌 !!
int main()
{
    vector<int> v = { 1, 2,3,4, 5, 3, 7, 8, 3, 10 };
    vector<int>::iterator p = remove(v.begin(), v.end(), 3); // 주어진 범위에서 3을 찾아서 삭제. list, vector, deque, 배열도 됨.
    // 컨테이너를 줄이지는 않음. 나머지 요소를 앞으로 당겨 옴.
    // p는 유효한 값 [다음] 위치를 돌려 줌.

    show(v);
    cout << *p << endl;

    v.erase(p, v.end()); // 여기서 실제 지우는 동작 함 !
    show(v);
}

#include "show.h"
// 총 4가지 변형이 존재 한다 : remove, remove_if, remove_copy, remove_copy_if

using namespace std::placeholders;
int main()
{
    vector<int> v = { 1, 2,3,4, 5, 3, 7, 8, 3, 10 };
    vector<int> v2(10);

    // 알고리즘의 4가지 변경.
    // vector<int>::iterator p = remove(v.begin(), v.end(), 3); // (1) 상수 버전
    // vector<int>::iterator p = remove_if(v.begin(), v.end(), foo); // (2) 조건자 버전 (함수)
    // vector<int>::iterator p = remove_if(v.begin(), v.end(), bind(modulus<int>(), _1, 2)); // (2) 조건자 버전(함수 객체)
    // vector<int>::iterator p = remove_if(v.begin(), v.end(), [](int a) { return a % 2 == 0;}); // (2) 조건자 버전(람다)
    //v.erase(p, v.end());

    // (3) 알고리즘의 복사 버전.
    vector<int>::iterator p = remove_copy(v.begin(), v.end(), v2.begin(), 3); // loop가 1회만 돈다. copy + remove 해도 되나 그보다 빠르다.
    // 여기서 p는 v2의 반복자
    // 반면, sort + copy는 성능차이가 많이 나기 때문에 sort_copy가 없다.
    // show(v);

    //show(v2);
    //v2.erase(p, v2.end());
    //show(v2);

    // (4) 조건자 복사 버전
    vector<int>::iterator p = remove_copy_if(v.begin(), v.end(), v2.begin(), [](int a) { return a % 2 == 0;});
}


#include "show.h"

int main() {
    int x[5] = { 1, 2, 3,4, 5 };
    int *p1 = find(x, x + 5, 3); // 상수 버전
    int *p2 = find_if(x, x + 5, [](int a) {return a % 2 == 0;}); // 조건자 버전
    cout << *p2 << endl;

    sort(x, x + 5); // < 연산으로 비교
    sort(x, x + 5, greater<int>()); // >로 비교
    // sort냐 sort_if냐 ? 왜 sort냐? find는 find_if는 같은 인자개수.
    // 조건자 버전이라도 parameter overloading 가능 하면 그대로 쓴다.
}


#include "show.h"
#include <numeric>

// (1) (2) (3) : algorithm 헤더
// (4) : numeric 헤더
int main() {
    int x[5] = { 1,2,3,4,5 };

   find(x, x + 5, 3); // 내부 구조 안바뀜. (1) 변경 불가 sequence 알고리즘.
   reverse(x, x + 5); // 내부 구조 바뀜.   (2) 변경 가능 sequence 알고리즘.
   sort(x, x + 5); //                     (3) 정렬 알고리즘.

   int n = accumulate(x, x + 5, 0); //    (4) 범용 수치 알고리즘.
   cout << n << endl;
}


#include "show.h"
#include <numeric>

// 범용 수치 알고리즘은 연산자를 바꿀 수 있다.
int main() {
    int x[5] = { 1,-2,3,4,5 };
    int y[5] = { 0 };
    int z[5] = { 0 };

    int n = accumulate(x, x + 5, 0); // 기본 버전 : + 사용
    int n2 = accumulate(x, x + 5, 1, multiplies<int>()); // * 사용 1~5까지의 곱 = 5!
    int n3 = accumulate(x, x + 5, 0, [](int a, int b) {return abs(a) + abs(b);}); // abs
    cout << n3 << endl;

    //partial_sum(x, x + 5, y);
    partial_sum(x, x + 5, y, multiplies<int>());
    for (int i = 0; i < 5; i++)
        cout << y[i] << " ";
}

// www.boost.org : 표준과 별도의 라이브러리 만들어보자.
// 아무도 이해할 수 없다 ㅠ;



#include "show.h"
#include <numeric>

int main() {
    vector<int> v = { 1, 2, 3,4, 5 };
    vector<int>::iterator p = copy(v.begin() + 1, v.end(), v.begin());

    show(v);
    v.erase(p, v.end());
    show(v);

    vector<int> v2 = { 1, 2, 3, 4, 5 };
    vector<int>::iterator p2 = copy(v2.begin(), v2.end()-1, v2.begin()+1);
    show(v2);

    // 예전엔 전부 1일 나왔음. 현재는 뒤에서부터 옴김.
    // 때문에 예전엔
    vector<int> v3 = { 1, 2, 3, 4, 5 };
    copy_backward(v3.begin(), v3.end() - 1, v3.end());
    show(v3);

    string s = "ABCD";
    do {
        cout << s << endl;
    } while (next_permutation(s.begin(), s.end()));
}


WebKit이나 Chromium에 쓰이는 RefCounted는 아예 RefCount<>를 상속 받아서 쓴다.
http://www.suninf.net/2013/11/refcounted-in-chrome.html
template <class T>
class RefCounted;

class MyFoo : public base::RefCounted<MyFoo>
{
protected:
  ~MyFoo() {}
  friend class base::RefCounted<MyFoo>;
};

댓글 없음:

댓글 쓰기