Overview s & osg::referenced Class & osg::ref_ptr<> Template Class 개요 OSG OSG::Referenced Class OSG::ref_ptr<> Template Class 2008년여름박경신 2 스마트포인터 () 는 C++ class 이다. syntax 와 semantics 상일반포인터와같다. memory management 나 locking 등좀더유용한일을수행한다. 가장간단한스마트포인터의예로 C++ standard 에있는 auto_ptr // auto_ptr 내부 template <class T> class auto_ptr T* ptr; public: explicit auto_ptr(t* p = 0) : ptr(p) ~auto_ptr() delete ptr; T& operator*() return *ptr; T* operator->() return ptr; //... ; 3 auto_ptr 를사용하여아래와같이바꿀수있다. void foo() MyClass* p(new MyClass); p->dosomething(); delete p; void foo() auto_ptr<myclass> p(new MyClass); p->dosomething(); // MyClass 의 DoSomething 함수를쓴다 // p 가알아서 scope 를벗어나면 clean up 해준다 4
template<class T> class SmartPtr // your own SmartPtr class public: explicit SmartPtr(T * pointee) : pointee_(pointee); SmartPtr& operator=(const SmartPtr& other); ~SmartPtr() delete pointee_; ; T& operator*() const // operator overloading return *pointee_; T* operator->() const // operator overloading return pointee_; operator T*() return pointee_; // conversion private: T *pointee_; class Widget public: void Fun(); ; int main() // SmartPtr<Widget> sp = new Widget() 과같은코드 SmartPtr<Widget> sp(new Widget); sp->fun(); (*sp).fun(); // (sp.pointee_)->fun() 과같은코드 ; 5 6 Why would I use? 기존의포인터쓰는방식은 Widget *p = new Widget; 즉, 변수 p 는 Widget 개체를위해할당된메모리를 point 하고또한 owns 하고있는것이다. 따라서, 프로그래머는 delete p 를불러서 Widget 개체를지우고또한메모리해제를하게된다. 때문에, p 로포인트된개체의 ownership 을해제하려면다음과같이써야한다. p = NULL; // assign something else to p 대부분의스마트포인터는다양한방법 (ownership transfer, reference counting, etc) 으로 ownership management 를해준다. 7 를사용해서우리가얻는것은무엇인가? Automatic cleanup 포인터를 free 할필요없다. Automatic initialization 스마트포인터를 NULL 로초기화할필요없다. Dangling pointers Exception safety try/catch 를고민하지않아도된다. Garbage collection Efficiency 스마트포인터가가용메모리의사용을좀더효과적으로만들어주며메모리할당과해제시간을줄어준다. STL containers 8
Why would I use? Why would I use? MyClass* p(new MyClass); MyClass* q = p; delete p; p->dosomething(); // Watch out! p is now dangling! p = NULL; // p is no longer dangling q->dosomething(); // Ouch! q is still dangling! // auto_ptr 은 copy 할때자신의 ptr 를 NULL 로만들어주어 dangling pointer 문제해결 template <class T> auto_ptr<t>& auto_ptr<t>::operator=(auto_ptr<t>& rhs) if (this!= &rhs) delete ptr; ptr = rhs.ptr; rhs.ptr = NULL; return *this; Create a new copy of the object pointed by p Ownership transfer: it transfer the responsibility for cleaning up ("ownership") from p to q Reference counting Reference linking class Base /*...*/ ; class Derived : public Base /*...*/ ; Base b; Derived d; vector<base> v; v.push_back(b); // OK v.push_back(d); // error // 다른 class 의개체를사용하고자한다면포인터를사용해야함 vector<base*> v; v.push_back(new Base); // OK v.push_back(new Derived); // OK too // you need to cleanup! for (vector<base*>::iterator i = v.begin(); i!= v.end(); ++i) delete *i; Copy on write 9 10 Why would I use? // 스마트포인터를사용하면자동으로 clean up 해줌 class Base /*...*/ ; class Derived : public Base /*...*/ ; Base b; Derived d; vector< linked_ptr<base> > v; v.push_back(new Base); // OK v.push_back(new Derived); // OK too // cleanup is automatic 일반적인시나리오경우, OSG 응용프로그램은 scene graph 의 root node 로포인트를하고있다. 그리고, root node 의포인터가 scene graph 의모든노드 (node) 로 reference 하고있다. 11 12
OSG 응용프로그램에서 scene graph 사용이끝나면메모리누수 (memory leak) 을피하기위해각노드마다차지하고있던메모리를 delete 해야한다. 전체 scene graph 를 traverse 하면서각노드 ( 와데이터 ) 를 delete 하는일은매우복잡한일이다. 이문제의해결방법으로 reference counted memory 를사용한 automated garbage collection 을사용해야한다. 모든 OSG 노드는 reference counted 되어있어서, reference count 가 0 으로줄면개체를 delete 한다. 따라서, OSG 의전체 scene graph 를지우려면 root node 포인터하나만지우면된다. 13 14 모든 OSG 노드와 scene graph 데이터클래스는공통베이스클래스인 osg::referenced 에서파생 (derived) 되어있다. osg::referenced 는 integer 타입 reference count 와이것의증가 (increment) 와감소 (decrement) 방법을가지고있다. OSG 는 osg::ref_ptr<> 라불리는 smart pointer template class 를정의하고있다. ref_ptr<> 변수를사용하여 heap 에할당되어있는 OSG 노드와 scene graph 데이터의주소를저장한다. 프로그램에서는 ref_ptr<> 변수로 referenced object 주소를할당했을때, reference count 가자동적으로증가한다. Referenced 에서파생한개체의포인터를저장한코드는반드시일반적인 C++ 포인터변수보다는 ref_ptr<> 에포인터로저장되어야한다. 그변수로할당된메모리를자동적으로 deletion해줄수있도록. ref_ptr<> 는 operator overloading을사용해서 ref_ptr<> 변수는일반적인 C++ 포인터변수와같이작동한다. ref_ptr<> 에서 operator ->() 와 operator *() 를 overloading 예외적인경우로, 일반적인 C++ 포인터변수를 Referenced에서파생한개체저장에 일시적 사용이가능하다 ( 만약그 heap memory address가궁극적으로 ref_ptr<> 에저장이된다면 ). 그러나일반적으로 ref_ptr<> 을사용하는것이안전하다. 15 16
OSG Referenced Class osg::referenced class reference counted block of memory 로구현되어있다. 모든 OSG nodes 와 scene graph data (state information, arrays of vertices, normals, texture coordinates 등을포함 ) 은이클래스로부터파생되었다. osg::referenced 클래스의 3 가지주요기능 Reference count member variable 인 _refcount 는클래스생성자 (constructor) 에 0 으로초기화되어있다. Public methods 인 ref() 와 unref() 는 _refcount 변수의증가 (incrementing) 과감소 (decrementing) 을담당하고있다. 그리고, unref() 는 _refcount 가 0 가되면개체를 delete 한다. 클래스소멸자 (destructor) 는 protected 와 virtual 이다. 즉, 스택 (stack) 에생성하거나명시적파괴 (explicit deletion) 는불가능하다. osg::ref_ptr<> template class Referenced 타입개체의스마트포인터 (smart pointer) 로구현되어있다. Reference count 를관리한다. Referenced 개체는마지막 ref_ptr<> 가 referencing 하고있는것이사라지게되면스스로자신을 delete 한다. ref_ptr<> 의주요기능 Private 포인터 _ptr 은 managed memory address 에저장된다. get() 함수를이용하여 _ptr 값을받는다. 프로그래머가 ref_ptr<> 을일반 C++ 포인터처럼사용할수있도록 operator->() 나 operator=() 같은다양한함수를제공한다. ref_ptr<> 이 NULL 이면 valid() 함수는 true 를반환한다. 17 18 프로그램에서 ref_ptr<> 변수로주소를할당할때, ref_ptr<> assignment operator 인 operator=() 가 Referenced::ref() 를불러서 reference count 를자동적으로증가시킨다. ref_ptr<> 변수가 Referenced::unref() 를부르면서 reference count 를감소할때의두가지경우는다음과같다. 클래스소멸자에서 ref_ptr<> deletion 할때 operator=() 에서 reassignment 할때 OSG의 osg::geode 클래스와 osg::group 클래스를사용하는예제 Geode는렌더링을위한 geometry를가지고있는 OSG leaf node이다. Group은여러개의children을가진하나의node이다. 예 : #include <osg/geode> #include <osg/ref_ptr> osg::ref_ptr<osg::geode> geode = new osg::geode; If (!geode.valid()) // ref_ptr<> is invalid, throw exception or display error 19 20
다른예제 #include <osg/geode> #include <osg/group> #include <osg/ref_ptr> // Create a new osg::geode object. The assignment to the // osg::ref_ptr<> increments the reference count to 1 osg::ref_ptr<osg::geode> geode = new osg::geode; // Assume grp is a pointer to an osg::group node. Group uses a // ref_ptr<> to point to its children, so addchild() again increments // the reference count to 2 grp->addchild(geode.get()); // The geode ref_ptr<> variable goes out of scope here. 21 // This decrements the reference count back to 1 전슬라이드의예제에서는코드에서오랫동안 geode 를유지하지않기때문에사실 ref_ptr<> 이필요하지않다. osg::group 부모노드에서내부적으로 ref_ptr<> 이 osg::geode 가차지하는메모리를관리하기때문에일반 C++ 로충분하다. // Create a new osg::geode object (regular C++ pointer). // Don t increments the child Geode increment the reference count 1 osg::geode *geode = new osg::geode; // Internal ref_ptr<> in Group increments the child node Geode // reference count to 1 grp->addchild(geode); 22 OSG에서 Referenced 개체는반드시 ref_ptr<> 변수에선언이되어야한다. 일반 C++ 포인터를사용할시주의할것. osg::geode * geode = new osg::geode; // don t do this, memory leak Referenced 에서파생된개체는명시적으로지울수없다. osg::geode geode1 = new osg::geode; delete geode1; // compile error: destructor is protected osg::geode geode2; osg::ref_ptr<osg::geode> geode = new osg::geode; // OK int i; osg::ref_ptr<int> rpi = &i; // NO! int is not derived from Referenced // top increments the Group count to 1 osg::ref_ptr<osg::group> top = new osg::group; // addchild() increments the Geode count to 1 top->addchild(new osg::geode); // The top ref_ptr goes out of scope, deleting both the // Group and Geode memory // compile error: destructor is protected 23 24
함수로개체의주소를반환 (Returning the address of an object) 할때주의해야한다. 잘못주소를반환했을경우, ref_ptr<> 가저장하고있는메모리주소가반환스택에돌려지기전에스코프를벗어나게된다. // Don t do this. It stores the address as the return value on the call // stack, but when the grp ref_ptr<> goes out of scope, the reference // count goes to zero and the memory is deleted. The calling function // is left with a dangling pointer. osg::group* creategroup() // Allocate a new Group node osg::ref_ptr<osg::group> grp = new osg::group; // return the new Group s address return *grp; 25 위의문제를해결하려면다음과같이사용해야한다. osg::ref_ptr<osg::group> creategroup() osg::ref_ptr<osg::group> grp = new osg::group; // return the new Group s address. This stores the Group address // in a ref_ptr<> and places the ref_ptr<> on the call stack as the // return value return grp.get(); 26 Summary Reference Referenced 에서파생된개체를 ref_ptr<> 변수로할당하는것은자동적으로 Reference::ref() 에서 reference count 를증가시킨다. 만약 ref_ptr<> 변수가다른것을포인트하게되거나또는지워지게되면 Referenced::unref() 를불러서 reference count 를감소시킨다. 그리고, 만약 reference count 가 0 가되면 unref() 는개체에할당된메모리를 delete 하게된다. Referenced 타입의개체를할당할때, 항상 ref_ptr<> 를지정해서 OSG 의메모리관리가정확히될수있도록한다. http://www.informit.com/articles/article.aspx?p=31529 http://ootips.org/yonat/4dev/smart-pointers.html 27 28