很久没有写博客了,很多工作中的总结都写在了自己的记事本里,比较杂,也没反映到博客上。刚去引擎组但却由于总公司的决策原因,引擎组当建立3个月就被撤销了,我也就没能继续做引擎了。部门被撤,当前也没任务,这些天就闲着看书。公司最近准备开IOS项目,我也经过很多思想上的挣扎,终于决定接受转向IOS等移动方向。
原因是虽然自己喜欢编程,喜欢做3d引擎,但是机会可遇不可求。能做3d引擎固然好,但是我当前还是个应届毕业生,也没有完整的做过任何商业项目,所以做个新项目也是不屈才的,更何况,新的领域,新的技术是IT行业的未来,如果能在未来领域里占有一席之地,或者要创业,移动都是值得的。
但归根结底,我觉得只要不断学习,做好每件看似简单的事情,不断的做到更好,就一定能让自己成为行业里的一面旗帜。我在做引擎中,首先是负责资源管理的,虽然引擎编程才刚刚开始,但是我对资源管理,特别是更底层的指针管理和内存分配等各种策略都琢磨了琢磨。这些天在看<<Game Engine Atchitecture>>,其中讲到StackMemory方式的分配策略,自己也模仿的实现了一把。
StackMemory方式的分配是首先在程序启动时申请一大块内存,然后其他对象要动态申请的时候,通过重载new操作符,使其在new的时候,转到StackMemory的分配策略(从低地址往高地址不断分配),然后当这个栈内存不需要时,StackMemory会调用所有曾经在它那分配过内存的对象的析构函数,然后将栈顶指针恢复到最低地址。这里讲的很粗糙,很有一些细节和策略需要注意。
通过StackMemory我们能够避免反复的new,delete,因为这些new,delete是很慢的操作(书上是这么说的,我目前相信大师没有骗我),特别是对于游戏软件来说,反复的new,delete必然会是cpu端的一大瓶颈,为了得到高帧率,高效率,我们有必要搞定这个问题。StackMemory只能搞定一小部门问题。
说了很多,其实是想说通过实现StackMemory,我觉得内存管理不错,于是想着解决大型软件中最常见的崩溃问题---野指针访问违例。我们经常将一个new出来的地址给了一个指针,然后这个指针有传给另一个指针,反复传递,等又一次我们想释放这个内存,确导致其他使用该地址的指针访问违例,程序崩溃。当然这个问题是由于结构混乱,导致指针在被销毁后还在访问,但是游戏软件确实太大,很多人参与写代码,光靠大家保持警惕,尽量少犯错时无法彻底避免的。所以我们应该想一个彻底的方法来避免野指针。我记得Torque中就有将指针封装,并串成链表管理的方式,我今天就模仿者写了一个SmartPointer来实现类似的功能。而且由于以前在实习公司看Torque时,对底层的管理很是模糊,所以自己实现一把也是很过瘾,很能够调理自己的。
上面的都算废话了,可以直接略过,主题在下面。
通过先实现一个简单的int*的SmartPointer,然后转换到模板SmartPointer,再反复修改,终于写出了一个简陋的SmartPointer(并且在这个过程中也弥补了自己一些基础知识的遗忘和不牢固)。把这个过程中对SmartPointer的需求列一下:
- 具有将指针串成链的功能,且在SmartPointer对象之间赋值时,能将这些SmartPointer隐式的加入到链表中,当SmartPointer对象被析构时应该冲链表中撤出来,并且如果此时链表中就剩这一个了,那么应该销毁SmartPointer所指向的真正的内存。
- SmartPointer不同类型的对象的之间应该可以赋值,因为他们虽然类型不同,但是他们存放的数据本身意义是相同的。
以下是我的SmartPointer的代码:
#ifndef __SMARTPOINTER__H__ #define __SMARTPOINTER__H__ template < typename T > class SmartPointer { public: SmartPointer() : m_Pointer( NULL ), m_Next( NULL ), m_Prev( NULL ) { } explicit SmartPointer( T p ) : m_Pointer( p ), m_Next( NULL ), m_Prev( NULL ) { } //////////////////////////////////////////////////////////////////////////for same type SmartPointer, SmartPointer( SmartPointer& Another ) { ( ( SmartPointer& )( *this ) ) = Another; } SmartPointer& operator = ( SmartPointer& Another ) { if( this != &Another ) { CheckIn( Another ); } return * this; } //////////////////////////////////////////////////////////////////////////for different SmartPointer template< typename AnotherType > SmartPointer( SmartPointer< AnotherType >& Another ) { ( ( SmartPointer& )( *this ) ) = ( SmartPointer< T >& )Another; } template< typename AnotherType > SmartPointer operator = ( SmartPointer< AnotherType >& Another ) { return ( ( SmartPointer& )( * this ) ) = ( SmartPointer< T >& )Another; } ////////////////////////////////////////////////////////////////////////// ~SmartPointer() { //if this SmartPointer is last SmartPointer in the list, we should destroy the SmartPointer list. if( !m_Prev && !m_Next ) { Destroy(); }//if not, we check this SmartPointer out. else { CheckOut(); } } void Destroy() { delete m_Pointer; ClearSmartPointerList(); } T GetPointer() { return m_Pointer; } protected: void CheckOut() { m_Pointer = NULL; if( m_Prev && m_Next )//in the middle { m_Prev->m_Next = m_Next; m_Next->m_Prev = m_Prev; } else if( m_Prev && !m_Next )//at the tail { m_Prev->m_Next = NULL; } else//at the head { m_Next->m_Prev = NULL; } } void CheckIn( SmartPointer& PrevSmartPointer ) { m_Pointer = PrevSmartPointer.m_Pointer; //following invocation order mustn't be changed. //things for current SmartPointer this->m_Next = PrevSmartPointer.m_Next; this->m_Prev = &PrevSmartPointer; //things for next SmartPointer if exists. if( PrevSmartPointer.m_Next ) { PrevSmartPointer.m_Next->m_Prev = this; } //things for previous SmartPointer PrevSmartPointer.m_Next = this; } void ClearSmartPointerList() { SmartPointer* ForwardPointer = m_Prev; SmartPointer* BackwardPointer = m_Next; SmartPointer* TempPointer = NULL; m_Pointer = NULL; m_Next = NULL; m_Prev = NULL; while( ForwardPointer ) { ForwardPointer->m_Pointer = NULL; ForwardPointer->m_Next = NULL; TempPointer = ForwardPointer->m_Prev; ForwardPointer->m_Prev = NULL; ForwardPointer = TempPointer; } while( BackwardPointer ) { BackwardPointer->m_Pointer = NULL; BackwardPointer->m_Prev = NULL; TempPointer = BackwardPointer->m_Next; BackwardPointer->m_Next = NULL; BackwardPointer = TempPointer; } } protected: T m_Pointer; SmartPointer* m_Next; SmartPointer* m_Prev; }; #endif
以上代码我经过简单测试通过,以下是我的测试代码:
int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hInstancePrev,LPSTR lpszCmdLine,int iShowCmd) { _CrtSetDbgFlag( _CRTDBG_LEAK_CHECK_DF | _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ) ); SmartPointer< int* > a( new int ); SmartPointer< int* > a1 = a; { SmartPointer< int* > a2; a2 = a; } SmartPointer< int* > a3 = a1; a3.Destroy(); SmartPointer< double* > b( new double ); SmartPointer< char* > b1 = b; SmartPointer< int* > b2; b2 = b; }
在编写SmartPointer过程中让我重新复习了一下对一个类的各种接口的规范以及泛型编程中一些注意事项,当然上面代码可能还会存在问题,还请读者直接指出,让我得以学习。
SmartPointer中用的算法其实只有list一个,所以实现起来并不难(这里就不讲了,上面也有简要注释)。但是很多时候明白看懂和自己做出来所得到的依旧不同,通过这次实践也让我更清晰底层的一些功能需求和学习了一些可能的实现方法。
通过看书,从大师那得知SmartPointer并不是一个很好的解决野指针的方法,他推荐boost库中的handle,我没用过。希望以后能见识见识。