Post List

레이블이 STL인 게시물을 표시합니다. 모든 게시물 표시
레이블이 STL인 게시물을 표시합니다. 모든 게시물 표시

2015년 2월 4일 수요일

bitset : Bit(비트) 단위로 저장 및 조작

bitset

Bit(비트) 단위로 Data를 저장하고 조작 할 수 있는 class이다.
(하지만 실제로 Bit 단위로 메모리를 사용하는 것은 아니다.)

사용하기 위해서는 bitset를 include 해 줘야 한다.

#include <bitset>

선언은

std::bitset<5> B;

bitset<Bit수> 로 선언을 하면 된다. (크기는 Compile time에 정해져야 한다. run time에 결정을 해서 정하지 못하는게 좀 아쉽다.)

배표적인 멤버함수는 다음과 같다.

bool bitset<N>::any() : 어떤거라도 켜져 있으면 true, 아니면 false 
bool bitset<N>::test(i) : i번째 값을 return
bitset<N> &bitset<N>::set(i,b) : i번째 값을 b로 설정한다. b는 적어주지 않으면 default 가 1이다.
bitset<N> &bitset<N>::reset(i) : i번째 값을 0으로 설정한다.
size_t bitset<N>::count() : 1의 갯수를 return 한다.

간단히 아래 예제만으로 모든 사용법을 다 익힐수 있다.

#include <iostream>
#include <bitset>

void main()
{
std::bitset<5> B;  // 00000

B.set(0);          // 00001
B.set(1);          // 00011
B.set(3);          // 01011

std::cout << B << std::endl; // 01011

bool b1 = B.any();   // true
bool b2 = B.test(2); // false

B.reset(3);         // 00011

std::cout << B << std::endl; // 00011
size_t s = B.count(); // 2
}

2015년 1월 26일 월요일

item 6. C++의 most vexing parse 를 조심하자.



file을 읽어서 그것을 list<int>에 넣는 것을 구현할 때 아래와 같은 Code가 나올수 있다.

ifstream dataFile("ints.dat");
list<int> data(istream_iterator<int>(dataFile),
               istream_iterator<int>());


하지만 위 Code는 오류없이 Compile 된다. 하지만 제대로 동작하지 않는다. 왜그런지 천천히 분석해보자.

아래 Code들은 double을 매개 변수로 받아 int를 return 하는 함수 f의 선언이다.

int f(double d);
int f(double(d));
int f(double);

모두 동일하다. 변수를 둘러싼 괄호 ()는 무시되며, 선언에서는 매개변수 이름을 적지 않아도 된다.

조금 다른 형태의 함수선언을 3개 더 보자. 아래는 함수 포인터를 매개변수로 받아서 int를 return하는 함수 g의 선언이다.

int g(double(&pf)());
int g(double pf());
int g(double());

모두 동일하다. *를 표시하지 않아도 뒤에 괄호() 로 인하여 함수 포인터인지 알수 있으며, (C , C++에서 유효한 문법이다.) 변수명의 생략이 가능하다.

f와 g의 선언중 ()로 된것을 보면 조금 감이 올 것이다. 그럼 이제 왜 처음에 본 저 Code가 제대로 동작하지 않는지 살펴보자.

ifstream dataFile("ints.dat");
list<int> data(istream_iterator<int>(dataFile),
               istream_iterator<int>());

- 첫번째 매개변수는 dafaFile이란 이름을 가지고 있으며 그 Type은 istream_iterator<int>이다. 괄호 () 는 Compiler가 무시한다.
- 두번째 매개변수는 이름을 가지고 있지 않으며 istream_iterator<int>를 return하는 함수포인터이다.

황당하겠지만, 이것이 C++ Compile 규칙에 어긋남이 하나도 없다. 

class Widget { … };
Widget w();

위 문장에서 w라는 Widget 객체가 만들어졌을까 ?
그냥 w라는 이름을 가진 Widget 를 return하는 함수를 선언한 것이다.

그럼 다시 원래 문제로 돌아와서 보자. 어떻게 저 문제를 해결 할 수 있을까 ?

첫번째 해결방법으로는 괄호 하나면 쉽게 해결이 된다.

list<int> data((istream_iterator<int>(dataFile)),
                istream_iterator<int>());

첫번째 매개변수를 괄호() 로 묶었다. 이렇게 하면 첫번째 매개변수에서 제대로 istream_iterator<int>이 생성되어서 정상적으로 동작한다. 하지만 불행하게도 모든 Compiler에서 제대로 되는건 아니었다. 이건 Compiler의 비위를 맞추려 코드의 이식성을 떨어뜨리는 근시안적인 방법이다.

Compiler에 구애받지 않고 쓸 수 있는 더 좋은 방법이 있다.
두번째 해결방법으로는 익명(anonymous) 객체 선언을 쓰지말고 객체 이름을 만들어 주는 것이다.

ifstream dataFile("ints.dat");
istream_iterator<int> dataBegin(dataFile);
istream_iterator<int> dataEnd;
list<int> data(dataBegin, dataEnd);

통상적인 STL Programming Style은 아니지만 차라리 혼란을 주는 Code보다는 나을 것이다.

2014년 12월 25일 목요일

[STL] shared_ptr, weak_ptr

shared_ptr

가장 많이 사용되는 C++ Standard Library의 Smart Pointer 이다.

 * 기능
 - 참조 Counter를 이용하여 Pointer의 복사가 가능하다. (auto_ptr의 경우에는 복사시 원래 Pointer가 NULL 처리됨)
 - delete 연산 필요없이 자동으로 메모리에서 해제된다. (이건 Smart Pointer니깐 당연한 것)

기능으로 따지면 위의 2가지 정도라고 볼 수 있지만, 정말 편리하게 사용 될 수 있다.
과거 불가능한것 까진 아니었지만, 구현이 복잡한 경우에 대해서 편리하게 적용이 가능하다.

 1. MFC Class 같이 복사할 수 없는 객체를 복사할 경우 유용 (Class내부 변수 or Collection 활용)
    (예제는 링크 참조 http://icysword.blog.me/140211570931)

 2. Multi-Thread 환경에서 안정성 확보 (Reference Count만 해당, 객체는 보장 안됨)

1. namespace와 Header File
[-] Collapse
#include <memory>
using namespace std::tr1;



2. 선언, 대입연산 및 Reference Count

 - 복사, 대입이 가능하다
 - Reference Count 증가 : 복사, 대입
 - Reference Count 감소 : Pointer가 소멸
 - shared_ptr Delete : Reference Count가 0이 되면 소멸
 - reset()을 이용하여 다른 객체를 참조하도록 설정이 가능하다. (그냥 대입해도 되므로 별로 쓸일은 없다.)

[-] Collapse
#include <memory>

class CObj {...};

void Func(std::tr1::shared_ptr<CObj> p_obj) { ... }

int main()
{
    std::tr1::shared_ptr<CObj> obj1(new CObj());    // Reference Count : 1
    std::tr1::shared_ptr<CObj> obj2(new CObj());    // Reference Count : 1

    // 복사 가능
    obj2(obj1);                                                       // 기존 obj2 delete, obj1의 Reference Count : 2
    // 대입 가능
    std::tr1::shared_ptr<CObj> obj3 = obj2;            // Reference Count : 3

    Func(obj3);                                                      // 호출시 Reference Count : 4
                                                                           // 실행후  Reference Count : 3

    // reset : 다른 객체를 참조하도록 설정, 인자가 없으면 참조 포기
    obj3.reset();                                                      // Reference Count : 2
    return 0;
}





3. 배열 사용

 - shared_ptr의 소멸은 객체애 대하여 delete 연산을 한다. (delete[]는 해주지 않는다.)
 - 즉 new [] 는 지원되나 delete [] 가 지원되지 않아서 Memory Leak이 발생한다.
 - deleter를 지정하여 해결이 가능하다.

[-] Collapse
#include <memory>

class CObj {...};

template<typename T>
struct ArrayDeleter { void operator() (T* p) { delete [] p; } };

void Func(std::tr1::shared_ptr<CObj> p_obj) { ... }

// 생성자의 두번째 인자로 deleter class를 넘기면 해결됨
std::tr1::shared_ptr<int> arrInt( new int[1024], ArrayDeleter<int>() );

4. Casting 및 참조

 - static_pointer_cast(), const_pointer_cast(), dynamic_pointer_cast() 로 Casting 가능
 - 참조
    * shared_ptr::get() : 주소 반환
    * shared_ptr::operator * : 객체 자체를 반환 (일반 Pointer 와 같음)
    * shared_ptr::operator-> : get()->와 같음 (일반 Pointer와 같음)
[-] Collapse
#include <memory>

class CParent {...};
class CChild : public CParent {...};

std::tr1::shared_ptr<CParent> P1( new CChild());

std::tr1::shared_ptr<CChild> C1 = static_pointer_cast<CChild>(P1); // Casting 하여 대입

// 참조방법 4가지
CParent* P2 = P1.get();
P1.get()->Func();
*(P1).Func();
P1->Func();





5. Circular References

 A, B 객체가 각각 서로에 대하여 shared_ptr을 가지고 있을 경우 Reference Counter가 0이 되지 않아서 메모리 해제가 안 될 수도 있다.
 개발을 하다보면 서로 Pointer를 가지고 있는 경우가 많이 발생하는데 이럴 경우에는 한쪽에 weak_ptr을 적용해야 한다.
[-] Collapse
#include <memory>
#include <vector>

class CMember;
typedef std::tr1::shared_ptr<CMember> CMemPtr;
class CDepartment;
typedef std::tr1::shared_ptr<CDepartment> CDeptPtr;

class CMember
{
public:
    void Set(const CDeptPtr& p_Dept) { m_Dept = p_Dept; )
protected:
    CDeptPtr m_Dept;
};

class CDepartment
{
public:
    void Add(const CMemPtr& p_Mem) { m_List.push_back(p_Mem); }
protected:
    std::vector<CMemPtr> m_List;
};

int main()
{
    CDeptPtr D1(new CDepartment);

    for (unsigned int i = 0; i < 10; i++)
    {
        CMemPtr M(new CMember);

        // Circular Refences 발생
        D->Add(M);
        M->Set(D);
    }

    D.reset(); // Refence Counter = 11인 상태에서 10으로 바뀌므로 메모리가 해제되지 않음
    return 0;
}






weak_ptr

 - shared_ptr을 참조만 할 뿐이지 Reference Counter를 증가시키지 않는다.
 - weak_ptr, shared_ptr로 부터 대입, 복사 가능 (일반 Pointer로부터의 대입은 불가)
 - shared_ptr로 convert 가능 (일반 Pointer로는 불가)

 위의 예제에서 weak_ptr을 사용하여 상호참조가 일어나지 않도록 해보겠다.
[-] Collapse
#include <memory>
#include <vector>

class CMember;
typedef std::tr1::shared_ptr<CMember> CMemPtr;
class CDepartment;
typedef std::tr1::shared_ptr<CDepartment> CDeptPtr;
typedef std::tr1::weak_ptr<CDepartment> CDeptWeakPtr;

class CMember
{
public:
    void Set(const CDeptPtr& p_Dept) { m_Dept = p_Dept; )
protected:
    CDeptWeakPtr m_Dept;
};

class CDepartment
{
public:
    void Add(const CMemPtr& p_Mem) { m_List.push_back(p_Mem); }
protected:
    std::vector<CMemPtr> m_List;
};

int main()
{
    CDeptPtr D1(new CDepartment);

    for (unsigned int i = 0; i < 10; i++)
    {
        CMemPtr M(new CMember);

        // Circular Refences 발생
        D->Add(M);
        M->Set(D); // 더이상 Reference Counter를 증가시키지 않는다.
    }

    D.reset(); // Refence Counter = 1인 상태에서 0으로 바뀌므로 메모리가 해제됨
    return 0;
}







참조 : http://sweeper.egloos.com/2826435