Article:boost shared ptr custom deallocatorFrom IdeA thinKING
요약boost::shared_ptr의 custom deallocator 사용법을 예제를 통해 알아봅니다. 예제 설명boost::shared_ptr의 custom deallocator의 사용법에 대해 알아보기 위해 아래와 같은 예제를 가정합니다.
프로그램의 메인 화면은 wxFrame에서 상속받아 사용하고 각각의 다이얼로그들은 wxDialog에서 상속받아 사용합니다. Gateway emulator에는 사용자의 간섭없이 시나리오대로 동작하는 ScenarioPhone과 wxDialog에서 상속받아 실제 전화기와 유사한 사용자 UI를 지원하는 GUIPhone이 있습니다. 마지막으로 wxFrame에서 상속받는 MainFrame 객체도 하나의 Phone의 역할을 할 수 있으며 이들은 모두 Phone 클래스에서 상속받습니다. 이들 Phone 객체들은 모두 heap에 dynamic하게 생성되어야 하며 Gateway와의 연결이 끊어지면 자동으로 소멸되어야 합니다. 하지만 MainFrame 객체는 메인 화면으로써의 역할을 하고 있기 때문에 연결이 끊어지더라도 소멸되어서는 안됩니다. 이 Phone 객체들의 관리를 용이하게 하기 위해 사용되는 Phone 객체들을 모두 std::vector<boost::shared_ptr<Phone> >으로 관리하고 연결이 끊어지면 간단히 clear()함수 호출로 모두 소멸시키고자 합니다. 문제는 wxDialog에서 상속받은 Phone 객체는 delete가 아니라 Destroy() 함수 호출을 통해 소멸되어야 하며 MainFrame은 소멸되어서는 안된다는 것입니다. 간단히 클래스들의 선언은 다음과 같습니다. class Phone class GUIPhone : public wxDialog, public Phone class MainFrame : public wxFrame, public Phone class ScenarioPhone : public Phone Custom deallocator 사용먼저 ScenarioPhone과 같이 wxWindows를 상속받지 않는 Phone 객체들은 일반적인 방법으로 boost::shared_ptr를 생성하여 std::vector 안에 넣습니다. (여기서 phone_s_는 std::vector<boost::shared_ptr<Phone> > 타입이라고 가정합니다.) boost::shared_ptr<Phone> p(new ScenarioPhone); phone_s_.push_back(p); 하지만 GUIPhone은 delete 대신 Destroy()함수가 호출되어야 합니다. 따라서 다음과 같은 custom deallocator를 GUIPhone 클래스안에 선언합니다. class GUIPhone : public wxDialog, public Phone { public: struct Deleter { void operator()(GUIPhone* p) { p->Destroy(); } }; ... }; 그리고 다음과 같이 custom deallocator를 사용하는 방법으로 boost::shared_ptr를 생성하여 std::vector안에 넣습니다. 이제 이 boost::shared_ptr 객체는 소멸시 delete 대신 사용자가 등록한 deallocator를 호출하게 됩니다. boost::shared_ptr<Phone> p(new GUIPhone, GUIPhone::Deleter()); phone_s_.push_back(p); 이렇게 boost::shared_ptr의 custom deallocator를 사용함으로써 하나의 std::vector안에 서로 다른 deallocator를 갖는 객체들을 저장하고 관리할 수 있습니다. 마지막으로 MainFrame과 같은 객체는 std::vector가 clear될때도 소멸되지 않아야 하며 이런 경우에는 다음과 같이 custom deallocator를 아무 일도 하지 않도록 작성하여 사용할 수 있습니다. class ScenarioPhone : public wxFrame, public Phone { public: struct Deleter { void operator()(ScenarioPhone* ) { // noop } }; ... }; 다음과 같이 등록합니다. 여기서는 등록하는 코드가 MainFrame 클래스의 멤버 함수안이라고 가정하고 this를 사용했습니다. boost::shared_ptr<Phone> p(this, ScenarioPhone::Deleter()); phone_s_.push_back(p); Tip: 특정 클래스의 객체를 stack에 만들지 못하게 하려면?마지막으로 custom deallocator를 사용한 tip으로 특정 클래스의 객체를 stack에는 만들어지지 않고 heap에만 생성되면서 항상 shared_ptr에 의해 수명이 관리되도록 하는 방법을 하나 소개합니다. class NoStack { struct Deleter { void operator()(NoStack* p) { delete p; } }; friend struct Deleter; public: static boost::shared_ptr<NoStack> create() { boost::shared_ptr<NoStack> p(new NoStack, NoStack::Deleter()); return p; } protected: virtual ~NoStack() {}; }; 먼저 소멸자를 protected로 선언함으로써 stack 변수로 선언이 불가능합니다. 다음으로 new로 생성하고 delete로 소멸시키고자 하는 경우에도 소멸자가 protected로 불가능합니다. 따라서 객체를 생성하고자 할 경우에는 항상 create()라는 static 함수를 사용해야 하며 이 객체의 수명은 자동으로 shared_ptr에 의해 관리됩니다. shared_ptr는 객체를 소멸시키기 위해 NoStack::Deleter라는 custom deallocator를 사용하게 되며 이 custom deallocator는 NoStack 클래스의 friend로 delete가 가능하게 됩니다. 마무리boost::shared_ptr의 custom deallocator에 대한 자세한 내용은 Boost 사이트나 Beyond the C++ Standard Library: An Introduction to Boost (ISBN: 0321133544)를 참고하세요. |

