원문: http://www2.research.att.com/~bs/C++0xFAQ.html#std-weak_ptr
“약한 포인터”는 shared_ptr을 이용하여 관리되는 자료 구조 내의 루프를 끊을 필요가 있는 것으로 종종 설명된다. weak_ptr을 다음과 같은 무언가를 가리키는 포인터라고 간주하는 게 낫다고 생각한다.
존재(하기만)한다면 접근할 필요가 있고 (누군가에 의해) 삭제될 수도 있고 마지막 사용 후에 (보통 이름없는 메모리 자원을 삭제하기 위해) 소멸자가 호출되도록 해야 할 무언가 옛날의 “소행성 게임(asteroid game)“의 구현을 고려해보라. 모든 소행성들은 “게임”에 소유되지만 각각의 소행성은 이웃하는 소행성들을 추적해야 하고 충돌을 다뤄야 한다. 충돌은 보통 하나 이상의 소행성의 파괴로 이어지기 마련이다. 각각의 소행성은 그것의 이웃에 있는 다른 소행성의 목록을 보관해야 한다. 그런 이웃 목록 위에 올려지면 소행성을 “살아있는” 것으로 보관할 필요는 없다는 걸 주의하라.(그래서 shared_ptr은 적당하지 않을 거다.) 다른 한 편으로는, 하나의 소행성은 다른 소행성이 그걸 쳐다보고 있는 동안에는(예를 들어, 충돌의 영향을 계산하기 위해) 파괴되어서는 안 된다. 그리고 분명히 소행성 소멸자는 (그래픽 시스템으로의 연결같은) 자원을 해제하기 위해 호출되어야 한다. 필요한 것은 여전히 온전한 소행성들의 목록과 잠시동안 소행성 하나를 걸어놓을 방법이다. weak_ptr은 단지 이런 걸 한다.
역주
어떤 소행성의 충돌 가능성이 높아지면 이웃 목록에 올라갈 것이고 이런 소행성들은 조만간 소멸자를 불러 자원을 해제할 가능성이 높은데(충돌되어 소멸되든 아니면 이웃에서 빠지든), 당장 삭제할 수는 없으니 weak_ptr로 가리키자는 의미임
void owner()
{
// ...
vector<shared_ptr<Asteroid>> va(100);
for (int i=0; i<va.size(); ++i) {
// ... 새로운 행성에 대해 이웃 행성을 계산함 ...
va[i].reset(new Asteroid(weak_ptr<Asteroid>(va[neighbor]));
launch(i);
}
// ...
}
reset()은 shared_ptr이 새로운 객체를 가리키도록 만드는 함수이다.
분명히, 나는 급진적으로 “owner”를 단순화시켰고 각각의 새로운 Asteroid에 하나의 이웃을 주었다. 중요한 건 Asteroid에게 그 이웃을 가리키는 weak_ptr을 주었다는 것이다. owner는 Asteroid가 바라볼 때마다(반대로는 아님) 공유되는 소유권을 표현하기 위해 shared_ptr을 보관한다. Asteroid를 위한 충돌 계산은 이처럼 보일 것이다.
void collision(weak_ptr<Asteroid> p)
{
if (auto q = p.lock()) { // p.lock은 p가 가리키는 객체에 대한 shared_ptr를 리턴함
// ... 행성이 아직 존재함: 충돌 계산을 수행함 ...
}
else {
// ... 이런! 가리키던 행성이 이미 소멸되었음: 고려할 필요 없음(이 행성에 대한 weak_ptr을 delete함)
}
}
owner가 게임을 종료하고 (소유권을 표현하는 shared_ptr을 소멸시키는 것으로) 모든 Asteroid를 삭제하려고 결정한다 하더라도 모든 Asteroid는 여전히 올바르게 끝내게 된다.(왜냐하면 p.lock() 후에 유효한 채로 남아 있으려는 shared_ptr을 들고 있기 때문에)weak_ptr의 사용이 “평범한” shared_ptr의 사용보다 더 많이 발견되기를 기대하고, unique_ptr이 더 단순한(그리고 더 효율적인) 소유권 개념을 표현하고 (그러므로) 더 좋은 지역적인 추론을 할 수 있게 해주기 때문에 unique_ptr이 shared_ptr의 사용보다 훨씬 더 인기있게 될 것을 바란다.