[중급] array 와 shared_ptr/weak_ptr
C++의 새로운 규격 「C++0x」에 추가 예정 라이브러리 인「TR1」의 개요를 Boost판을 사용 하여 해설합니다(Visual Studio 2008에도 추가 패키지로서 공급될 예정).
제1회는 array 와 shared_ptr/weak_ptr에 대해서 .
괴로운 메모리 관리 - shared_ptr / weak_ptr
C/C++ 에서 메모리 관리는 프로그래머에게 맡겨져 있습니다. 귀찮은 것으로 프로그래머의 약간의 미스가 대 사고로 연결 되는 것이 고민의 씨앗입니다 .
포인터는 귀찮다…
string* p = new string( "one" );
string* q = new string( "two" );
p = q; // p,q 모두 "two" 를 가리킨다. "one"은 미아 로.
delete p;
delete q; // "two"를 다중delete!
std::auto_ptr의 한계
표준 C++라이브러리는 메모리 관리 를 편하게 하는 std::auto_ptr 를 제공하고 있습니다.
auto_ptr 이라면
#include
#include ring>
#include <memory> // std::auto_ptr<T>
using namespace std;
class item {
private :
string value_;
public :
item( const char * v= "???" ) : value_(v) {
cout << "item(" << value_ << ") ctor\n" ;
}
~item() { cout << "item(" << value_ << ") dtor\n" ; }
string value() const { return value_; }
};
int main() {
auto_ptr<item> p( new item( "one" ));
auto_ptr<item> q( new item( "two" ));
p = q; // p는 가리키고 있었 던 "one"을 delete해, "two"를 가리킨다. q는 null 이 된다.
cout << "p points to " << (p.get() ? p->value() : "(null)" ) << endl;
cout << "q points to " << (q.get() ? q->value() : "(null)" ) << endl;
}
실행 결과
item(one) ctor
item(two) ctor
item(one) dtor
p p oints to two
q points to (null)
item(two) dtor
실행 결과가 나타내 보이는 대로 constructor 와 소멸자의 수가 일치하고 있을 테니 delete 잊음 이나 다중 delete가 발생하고 있지 않는 것을 압니다. auto_ptr 은 소멸에 그것이 가리키는 포인터를 delete 하므로 명시적으로 delete 할 필요가 없습니다.
다만 auto_ptr 사이의 복사(대입)를 하면 포인터의 소유권(=삭제 책임)이 이동 하여 복사 처(source)에서는 포인터가 없어집니다(실행 결과의 q가 "two"를 가리키고 있지 않아요). 그 때문에 복수의 auto_ptr 가하나의 인스턴스를 가리켜 공유할 수 없습니다.
auto_ptr 에서는 인스턴스를 공유할 수 없다
class person {
string name_;
public :
person(string n) : namae_(n) {}
auto_ptr child; // 아이
};
person sazae( "소라" );
person masuo( "마스오" );
auto_ptr tara( new person( "타라" ));
// "타라"를 "소라"와 "마스오"의 아이로 하고 싶지만
sazae.child = tara; // 이 순간"타라"의 소유권이tara로부터
// sazae.child로 이동(양도된다)
masuo.child = tara; // masuo.child 는 "타라"를 가리킬 수 없다
new된 인스턴스를 자동적으로 delete 해 주는 auto_ptr 은 편리 한 것은 틀림 없습니다만 이 제한이 있기 때문에 용도가 한정되어 버립니다.
std::tr1::shared_ptr - 공유 포인터
TR1 에서 새롭게 추가된 shared_ptr 은 참조 카운트라고 하는 것으로 인스턴스를 관리합니다. shared_ptr 은 그 내부에그 인스턴스를 참조하고 있는 shared_ptr의 총수를 보관 유지하고 있습니다. shared_ptr 의 소멸자는 내부의 참조수를 -1하여 그것이 0이 되었을 때 인스턴스가 어디에서도 참조되지 않게 되었다 라고 판단 하고 인스턴스를 delete 합니다.
shared_ptr
#include <iostream>
#include <string>
#include <boost/tr1/memory.hpp> // std::tr1::shared_ptr
using namespace std;
class item {
private :
string value_;
public :
item( const char * v= "???" ) : value_(v) {
cout << "item(" << value_ << ") ctor\n" ;
}
~item() { cout << "item(" << value_ << ") dtor\n" ; }
string value() const { return value_; }
};
int main() {
// p1 은 "something"을 가리킨다. 참조수:1
tr1::shared_ptr<item> p1( new item( "something" ));
cout << "p1->value() = " << p1->value() << endl;
{
// p2도 "something"을 가리킨다. 참조수:2
tr1::shared_ptr<item> p2 = p1;
cout << "p2->value() = " << p2->value() << endl;
// 여기서p2 가사라진다. 참조 회수:1
}
cout << "p1->value() = " << p1->value() << endl;
// 여기서p1 이사라진다. 참조수:0되어 "something" 가 delete된다
}
실행 결과item(something) ctorp1->value() = somethingp2->value() = somethingp1->value() = somethingitem(something) dtorstd::tr1:weak_ptr 약 참조 포인터 shared_ptr 을 사용하는 것에 의해서 번잡한 메모리관리 로부터 해방됩니다만 이것이라도 아직 순환 참조 라고 하는 문제가 남아 있습니다.
순환 참조란
#include <iostream>
#include <string>
#include <boost/tr1/memory.hpp> // std::tr1::shared_ptr
class Person {
public :
string name; // 이름
tr1::shared_ptr spouse; // 배우자
Person(string n) : name(n) {}
void info() const {
cout << "My name is " << name
<< " and my spouse is " << spouse->name << endl;
}
};
int main() {
// one 은 "adam"을 가리킨다. 참조수:1
tr1::shared_ptr<Person> one( new Person( "adam" ));
{
// two 는 "eve"을 가리킨다: 참조수:1
tr1::shared_ptr<Person> two( new Person( "eve" ));
one->spouse = two; // "adam"의 아내는"eve" 참조수:2
two->spouse = one; // "eve"의 남편은"adam" 참조수:2
one->info();
two->info();
// 여기서two 가사라진다. 참조수:1 ... 0은 아니기 때문에 "eve" 는 delete 되지 않는다
}
one->info();
// 여기one 이사라진다. 참조수:1 ... 0은 아니기 때문에 "adam" 는delete 되지 않는다
}
실행 결과
My name is adam and my spouse is eve
My name is eve and my spouse is adam
My name is adam and my spouse is eve
이 예 와 비슷하게 복수의 shared_ptr 이 서로를 서로 참조해 루프를 형성하면(순환 하는 ) 참조수가 0이 되는 것이 없기 때문에 인스턴스가 delete 되지 않고 남아 버립니다.
이 문제를 해소하기 위해 TR1은 한층 더 하나 더 weak_ptr 을 제공합니다. weak_ptr 은 shared_ptr 가보관 유지하는 참조수의 증감에 관여하지 않습니다. 또한 weak_ptr 의 멤버 expired() 에 의해서 인스턴스의 소실을 알 수 있습니다.
weak_ptr에 의한 해결
#include <iostream>
#include <string>
#include <boost/tr1/memory.hpp>
// std::tr1::shared_ptr, std::tr1::weak_ptr
class Person {
public :
string name;
tr1::weak_ptr<spouse> spouse;
Person(string n) : name(n) {}
void info() const {
cout << "My name is " << name
<< " and my spouse " ;
if ( spouse.expired() ) // 인스턴스의 유무를 판단한다
cout << "has gone...\n" ;
else
cout << spouse.lock()->name << endl;
}
};
int main() {
// one 은 "adam"을 가리킨다. 참조수:1
tr1::shared_ptr<Person2> one( new Person2( "adam" ));
{
// two 는 "eve"를 가리킨다: 참조수:1
tr1::shared_ptr<Person2> two( new Person2( "eve" ));
// weak_ptr은 참조수의 증감에 관여하지 않는다
one->spouse = two; // "adam"의 아내는 "eve" 참조수:1
two->spouse = one; // "eve"의 남편은 "adam" 참조수:1
one->info();
two->info();
// 여기서 two가 사라진다. 참조수:0 이 되어 "eve" 는 delete 된다
}
one->info();
// 여기서 one이 사라진다. 참조수:0 이 되어 "adam" 은 delete 된다
}
실행 결과
My name is adam and my spouse eve
My name is eve and my spouse adam
My name is adam and my spouse has gone...
정리
차기 C++규격 「C++0x」 에서 확장 예정의 라이브러리 「TR1」로부터 array 와 shared_ptr /weak_ptr 를 소개했습니다.이것들을 활용하는 것으로 C++에 대해 귀찮은 메모리 관리가 훨씬 편해지겠지요.
TR1에는 그 외에도 편리한 클래스가 다수 수록되고 있습니다. 속편을 기대하세요.
** Boost 라이브러리의 1.34와 1.35 간에 _ptr 사용시 다른 점이 있습니다. 탬플릿 인자를 넣어줘야 되더군요.
shared_ptr<T> p(new Y); 식으로 말이죠. 위의 예문을 1.35 버전을 근거로 수정합니다. **
번역 후기........
번역된 글을 퍼 갈 때는 꼭 아래의 글을 같이 복사 해 주세요.
번역은 원 저자에게 허락을 받은 것은 아니므로 상업적으로 사용할 경우에는 꼭 원저자에게 허락을 받기를 바랍니다.
출처 : http://codezine.jp/a/article/aid/1937.aspx
번역 : 최흥배 ( jacking75@gmail.com ).
(주) 다이슨 파이퍼스튜디오에서 서버 프로그래머로 근무 중.
'프로그래밍' 카테고리의 다른 글
[펌] Boost 로 C++0x의 라이브러리 「TR1」을 미리 사용 해 보자 (4) (0) | 2012.08.13 |
---|---|
[펌] Boost 로 C++0x의 라이브러리 「TR1」을 미리 사용 해 보자 (3) (0) | 2012.08.13 |
[펌] Lock-Free Queue (0) | 2012.08.11 |
Proactor pattern (0) | 2012.08.11 |
[펌] 코드 정리 하기 (0) | 2012.08.11 |