题记:对于技术,我们大可不必挖得那么深,但一定要具备可以挖得很深的能力

 
问题的由来
 
怎么样使用 Cocos2d-x 快速开发游戏,方法很简单,你可以看看其自带的例程,或者从网上搜索教程,运行起第一个 Scene HelloWorldScene,然后在 HelloWorldScene 里面写相关逻辑代码,添加我们的层、精灵等 ~ 我们并不一定需要知道 Cocos2d-x 是如何运行或者在各种平台之上运行,也不用知道 Cocos2d-x 的游戏是如何运行起来的,它又是如何渲染界面的 ~~~
 
我们只用知道 Cocos2d-x 的程序是由 AppDelegate 的方法 applicationDidFinishLaunching 开始,在其中做些必要的初始化,并创建运行第一个 CCScene 即可,正如我们第一次使用各种编程语言写 Hello World! 的程序一样,如 Python 打印:
  1. print('Hello World!')
 
我们可以不用关心其是怎么实现的,我们只要知道这样就能打印一句话就够了,这就是 封装所带来的好处 。
 
Cocos2d-x 自带的例程已经足够丰富,但是有些问题并不是看看例子,调用其方法就能明白的事情,在这里一叶遇到了如下问题:
  1. // AppDelegate.cpp 文件
  2. AppDelegate::AppDelegate()
  3. {
  4. CCLog("AppDelegate()");     // AppDelegate 构造函数打印
  5. }
  6. AppDelegate::~AppDelegate()
  7. {
  8. CCLog("AppDelegate().~()");     // AppDelegate 析构函数打印
  9. }
  10. // 程序入口
  11. bool AppDelegate::applicationDidFinishLaunching()
  12. {
  13. // initialize director
  14. CCDirector *pDirector = CCDirector::sharedDirector();
  15. pDirector->setOpenGLView(CCEGLView::sharedOpenGLView());
  16. // 初始化,资源适配,屏幕适配,运行第一个场景等代码
  17. ...
  18. ...
  19. ...
  20. return true;
  21. }
  22. void AppDelegate::applicationDidEnterBackground()
  23. {
  24. CCDirector::sharedDirector()->pause();
  25. }
  26. void AppDelegate::applicationWillEnterForeground()
  27. {
  28. CCDirector::sharedDirector()->resume();
  29. }
 
此时我并不知道程序运行时,何时调用 AppDelegate 的构造函数,析构函数和程序入口函数,我们只要知道,程序在这里调用了其构造函数,然后进入入口函数执行其过程,最后再调用其析构函数即可。然而事与愿违,在实际执行的过程中,发现程序只调用其构造函数和入口函数,而直到程序结束运行,都 没有调用其析构函数。要验证此说法很简单,只要如上在析构函数中调用打印日志便可验证。
 
发生这样的情况,让我 在构造函数创建[资源],并且在析构函数中释放[资源] 的想法不能完成!!! 我们知道它是从哪里开始运行,但却不知道它在哪里结束!疑问,唯有疑问!
 
两个入口
 
程序入口的概念是相对的,AppDelegate 作为跨平台程序入口,在这之上做了另一层的封装,封装了不同平台的不同实现,比如我们通常认为一个程序是由 main 函数开始运行,那我们就去找寻,我们看到了在 proj.linux 目录下存在 main.cpp 文件,这就是我们要看的内容,如下:
  1. #include "main.h"
  2. #include "../Classes/AppDelegate.h"
  3. #include "cocos2d.h"
  4. #include <stdlib.h>
  5. #include <stdio.h>
  6. #include <unistd.h>
  7. #include <string>
  8. USING_NS_CC;
  9. // 500 is enough?
  10. #define MAXPATHLEN 500
  11. int main(int argc, char **argv)
  12. {
  13. // get application path
  14. int length;
  15. char fullpath[MAXPATHLEN];
  16. length = readlink("/proc/self/exe", fullpath, sizeof(fullpath));
  17. fullpath[length] = '\0';
  18. std::string resourcePath = fullpath;
  19. resourcePath = resourcePath.substr(0, resourcePath.find_last_of("/"));
  20. resourcePath += "/../../../Resources/";
  21. // create the application instance
  22. AppDelegate app;
  23. CCApplication::sharedApplication()->setResourceRootPath(resourcePath.c_str());
  24. CCEGLView* eglView = CCEGLView::sharedOpenGLView();
  25. eglView->setFrameSize(720, 480);
  26. //    eglView->setFrameSize(480, 320);
  27. return CCApplication::sharedApplication()->run();
  28. }
 
在这里我们看见了程序的真正入口,包含一个 main 函数,从此进入,执行 cocos2d-x 程序。
 
我们看到 main 就知道其是入口函数,那么没有 main 函数就没有入口了吗?显然不是,以 Android 平台启动 cocos2d-x 程序为例。我们找到 Android 平台与上面 等价 的入口点,proj.android/jni/hellocpp/main.cpp:
  1. #include "cocos2d.h"
  2. #include "AppDelegate.h"
  3. #include "platform/android/jni/JniHelper.h"
  4. #include <jni.h>
  5. #include <android/log.h>
  6. #define  LOG_TAG    "main"
  7. #define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
  8. using namespace cocos2d;
  9. extern "C"
  10. {
  11. jint JNI_OnLoad(JavaVM *vm, void *reserved)
  12. {
  13. JniHelper::setJavaVM(vm);
  14. return JNI_VERSION_1_4;
  15. }
  16. void Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv*  env, jobject thiz, jint w, jint h)
  17. {
  18. if (!CCDirector::sharedDirector()->getOpenGLView())
  19. {
  20. CCEGLView *view = CCEGLView::sharedOpenGLView();
  21. view->setFrameSize(w, h);
  22. AppDelegate *pAppDelegate = new AppDelegate();
  23. CCApplication::sharedApplication()->run();
  24. }
  25. else
  26. {
  27. ccDrawInit();
  28. ccGLInvalidateStateCache();
  29. CCShaderCache::sharedShaderCache()->reloadDefaultShaders();
  30. CCTextureCache::reloadAllTextures();
  31. CCNotificationCenter::sharedNotificationCenter()->postNotification(EVNET_COME_TO_FOREGROUND, NULL);
  32. CCDirector::sharedDirector()->setGLDefaultValues();
  33. }
  34. }
 
我们并没有看到所谓的 main 函数,这是由于不同的平台封装所以有着不同的实现,在 Android 平台,默认是使用 Java 开发,可以使用 Java 通过 Jni 调用 C++ 程序,而这里也正式如此。我们暂且只需知道,由 Android 启动一个应用,通过各种峰回路转,最终执行到了 Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit 函数,由此,变开始了我们 cocos2d-x Android 平台的程序入口处。对于跨平台的 cocos2d-x 来说,除非必要,否则可不必深究其理,比如想要使用 Android 平台固有的特性等,那就需要更多的了解 Jni 使用方法,以及 Android 操作系统的更多细节。
 
所以说程序的入口是相对的,正如博文开始的 print('Hello World') 一样,不同的语言,不同平台总有着不同的实现。
 
这里我们参考了两个不同平台的实现, Linux 和 Android 平台 cocos2d-x 程序入口 main.cpp的实现,那么其它平台呢,如 iOS ,Win32 等 ~~~ 殊途同归,其它平台程序的入口必然包含着其它平台的不同 封装实现 ,知道有等价在此两平台的程序入口即可。而通过这两个平台也足够解决我们的疑问,程序的开始与结束 ~
 
问题的推测
 
我们就从 Linux 和 Android 这两个平台的入口函数开始,看看 cocos2d-x 的执行流程到底为何?何以发生只执行了 AppDelegate 的构造函数,而没有析构函数。在查看 cocos2d-x 程序代码时,我们只关注 必要的 内容,何谓 必要,只要能解决我们此时的疑问即可!在两个平台的入口函数,我们看到如下内容:
  1. // Linux 平台关键代码
  2. int main(int argc, char **argv)
  3. {
  4. // 初始化等内容
  5. ...
  6. ...
  7. // 创建 app 变量
  8. AppDelegate app;
  9. ...
  10. ...
  11. // 执行 核心 run() 方法
  12. return CCApplication::sharedApplication()->run();
  13. }
  14. // Android 平台关键代码
  15. void Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv*  env, jobject thiz, jint w, jint h)
  16. {
  17. if (!CCDirector::sharedDirector()->getOpenGLView())
  18. {
  19. CCEGLView *view = CCEGLView::sharedOpenGLView();
  20. view->setFrameSize(w, h);
  21. // 创建 AppDelegate 对象
  22. AppDelegate *pAppDelegate = new AppDelegate();
  23. // 执行 核心 run() 方法
  24. CCApplication::sharedApplication()->run();
  25. }
  26. else
  27. {
  28. ...
  29. ...
  30. }
  31. }
 
不同的平台,却实现相同操作,创建 AppDelegate 变量和执行 run 方法。下面将以 Linux 平台为例,来说明程序是如何开始与结束的,因为 Linux 的内部实现要简单一点,而 Android 平台的实现稍显麻烦,Jni 之间来回调用,对我们理解 cocos2d-x 的执行流程反而有所 阻碍,况且 cocos2d-x 本身就是跨平台的程序。不必拘泥于特有平台的专有特性。
 
程序的流程 (这里以 Linux 的实现为主,其它平台触类旁通即可)
 
AppDelegate 与 CCApplication
 
我们从 main.cpp 中 CCApplication::sharedApplication()->run(); 这一句看起,这一句标志着, cocos2d-x 程序正式开始运行,一点点开始分析,我们定位到 sharedApplication() 方法的实现,这里只给出 必要 的代码,具体看一自己直接看源码:
  1. // [cocos2dx-path]/cocos2dx/platform/linux/CCApplication.cpp
  2. ...
  3. // 此变量为定义了一个 CCApplication 的静态变量,也及时自己类型本身,实现单例模式
  4. CCApplication * CCApplication::sm_pSharedApplication = 0;
  5. ...
  6. // 构造函数,将所创建的 对象直接付给其静态变量
  7. CCApplication::CCApplication()
  8. {
  9. // 断言在此决定着此构造函数只能运行一次
  10. CC_ASSERT(! sm_pSharedApplication);
  11. sm_pSharedApplication = this;
  12. }
  13. CCApplication::~CCApplication()
  14. {
  15. CC_ASSERT(this == sm_pSharedApplication);
  16. sm_pSharedApplication = NULL;
  17. m_nAnimationInterval = 1.0f/60.0f*1000.0f;
  18. }
  19. // run 方法,整个 cocos2d-x 的主循环在这里开始
  20. int CCApplication::run()
  21. {
  22. // 首次启动调用初始化函数
  23. if (! applicationDidFinishLaunching())
  24. {
  25. return 0;
  26. }
  27. // 游戏主循环,这里 Linux 的实现相比其它平台的实现,简单明了
  28. for (;;) {
  29. long iLastTime = getCurrentMillSecond();
  30. // 在循环之内调用每一帧的逻辑,组织并且控制 cocos2d-x 之中各个组件
  31. CCDirector::sharedDirector()->mainLoop();
  32. long iCurTime = getCurrentMillSecond();
  33. // 这里的几个时间变量,可以控制每一帧所运行的 最小 时间,从而控制游戏的帧率
  34. if (iCurTime-iLastTime<m_nAnimationInterval){
  35. usleep((m_nAnimationInterval - iCurTime+iLastTime)*1000);
  36. }
  37. }
  38. // 注意,这里的 for 循环,并没有退出循环条件,这也决定着 run() 方法永远也不会返回
  39. return -1;
  40. }
  41. // 方法直接返回了静态对象,并且做了断言,也既是在调用此方法之前,
  42. // 必须事先创建一个 CCApplication 的对象,以保证其静态变量能够初始化,否则返回空
  43. CCApplication* CCApplication::sharedApplication()
  44. {
  45. CC_ASSERT(sm_pSharedApplication);
  46. return sm_pSharedApplication;
  47. }
 
从上面的内容可以看出,从 sharedApplication() 方法,到 run() 方法,在这之前,我们需要调用到它的构造函数,否则不能运行,这就是为什么在 CCApplication::sharedApplication()->run(); 之前,我们首先使用了 AppDelegate app; 创建 AppDelegate 变量的原因! 嗯 !! AppDelegate 和 CCAppliation 是什么关系! 由 AppDelegate 的定义我们可以知道,它是 CCApplication 的子类,在创建子类对象的时候,调用其构造函数的同时,父类构造函数也会执行,然后就将 AppDelegate 的对象赋给了 CCApplication 的静态变量,而在 AppDelegate 之中我们实现了 applicationDidFinishLaunching 方法,所以在 CCApplication 中 run 方法的开始处调用的就是 AppDelegate 之中的实现。而我们在此方法中我们初始化了一些变量,创建了第一个 CCScene 场景等,之后的控制权,便全权交给了 CCDirector::sharedDirector()->mainLoop(); 方法了。
 
(这里的实现机制,不做详细说明,简单说来:applicationDidFinishLaunching 是由 CCApplicationProtocol 定义,CCApplication 继承, AppDelegate 实现的 ~)
 
比较重要的所在,for 循环并没有循环退出条件,所以 run 方法永远不会返回。那么是怎么结束的呢!要学会存疑!
 
从 CCApplication 到 CCDirector
 
cocos2d-x 程序已经运行起来了,我们继续下一步,mainLoop 函数:
  1. // [cocos2dx-path]/cocos2dx/CCDirector.cpp
  2. ...
  3. // 定义静态变量,实现单例模式
  4. static CCDisplayLinkDirector *s_SharedDirector = NULL;
  5. ...
  6. // 返回 CCDirector 实例
  7. CCDirector* CCDirector::sharedDirector(void)
  8. {
  9. // 判断静态变量,以保证只有一个实例
  10. if (!s_SharedDirector)
  11. {
  12. s_SharedDirector = new CCDisplayLinkDirector();
  13. s_SharedDirector->init();
  14. }
  15. // CCDisplayLinkDirector 为 CCDirector 的子类,这里返回了其子类
  16. return s_SharedDirector;
  17. }
  18. // mainLoop 方法的具体实现
  19. void CCDisplayLinkDirector::mainLoop(void)
  20. {
  21. // 此变量是我们需要关注,并且跟踪的,因为它决定着程序的结束时机
  22. if (m_bPurgeDirecotorInNextLoop)
  23. {
  24. m_bPurgeDirecotorInNextLoop = false;
  25. // 运行到此,说明程序的运行,已经没有逻辑代码需要处理了
  26. purgeDirector();
  27. }
  28. else if (! m_bInvalid)
  29. {
  30. // 屏幕绘制,并做一些相应的逻辑处理,其内部处理,这里暂且不做过多探讨
  31. drawScene();
  32. // 这里实现了 cocos2d-x CCObject 对象的内存管理机制,对此有兴趣者,可以深入下去
  33. CCPoolManager::sharedPoolManager()->pop();
  34. }
  35. }
  36. // 弹出场景 CCScene
  37. void CCDirector::popScene(void)
  38. {
  39. CCAssert(m_pRunningScene != NULL, "running scene should not null");
  40. m_pobScenesStack->removeLastObject();
  41. unsigned int c = m_pobScenesStack->count();
  42. if (c == 0)
  43. {
  44. // 如果没有场景,调用 end() 方法
  45. end();
  46. }
  47. else
  48. {
  49. m_bSendCleanupToScene = true;
  50. m_pNextScene = (CCScene*)m_pobScenesStack->objectAtIndex(c - 1);
  51. }
  52. }
  53. void CCDirector::end()
  54. {
  55. // 在 end 方法中,设置了变量为 true,这所致的结果,在 mainLoop 函数中,达成了运行 purgeDirector 方法的条件
  56. m_bPurgeDirecotorInNextLoop = true;
  57. }
  58. // 此方法做些收尾清理的工作
  59. void CCDirector::purgeDirector()
  60. {
  61. ...
  62. if (m_pRunningScene)
  63. {
  64. m_pRunningScene->onExit();
  65. m_pRunningScene->cleanup();
  66. m_pRunningScene->release();
  67. }
  68. // 做一些清理的工作
  69. ...
  70. // OpenGL view
  71. // ###此句代码关键###
  72. m_pobOpenGLView->end();
  73. m_pobOpenGLView = NULL;
  74. // delete CCDirector
  75. release();
  76. }
  77. // 设置 openglview
  78. void CCDirector::setOpenGLView(CCEGLView *pobOpenGLView)
  79. {
  80. CCAssert(pobOpenGLView, "opengl view should not be null");
  81. if (m_pobOpenGLView != pobOpenGLView)
  82. {
  83. // EAGLView is not a CCObject
  84. delete m_pobOpenGLView; // [openGLView_ release]
  85. // 为当前 CCDirector m_pobOpenGLView  赋值
  86. m_pobOpenGLView = pobOpenGLView;
  87. // set size
  88. m_obWinSizeInPoints = m_pobOpenGLView->getDesignResolutionSize();
  89. createStatsLabel();
  90. if (m_pobOpenGLView)
  91. {
  92. setGLDefaultValues();
  93. }
  94. CHECK_GL_ERROR_DEBUG();
  95. m_pobOpenGLView->setTouchDelegate(m_pTouchDispatcher);
  96. m_pTouchDispatcher->setDispatchEvents(true);
  97. }
  98. }
 
游戏的运行以场景为基础,每时每刻都有一个场景正在运行,其内部有一个场景栈,遵循后进后出的原则,当我们显示的调用 end() 方法,或者弹出当前场景之时,其自动判断,如果没有场景存在,也会触发 end() 方法,以说明场景运行的结束,而游戏如果没有场景,就像演出没有了舞台,程序进入最后收尾的工作,通过修改变量 m_bPurgeDirecotorInNextLoop 促使在程序 mainLoop 方法之内调用 purgeDirector 方法。
 
CCEGLView 的收尾工作
 
purgeDirector 方法之内,通过猜测与排查,最终定位到 m_pobOpenGLView->end(); 方法,在这里结束了 cocos2d-x 游戏进程。而 m_pobOpenGLView 有时何时赋值,它的具体实现又在哪里呢?我们可以在 AppDelegate 的 applicationDidFinishLaunching 方法中找到如下代码:
  1. // AppDelegate.cpp
  2. CCDirector *pDirector = CCDirector::sharedDirector();
  3. pDirector->setOpenGLView(CCEGLView::sharedOpenGLView());
 
我们终于走到最后一步,看 CCEGLView 是如果负责收尾工作的:
  1. // [cocos2dx-path]/cocos2dx/platform/linux.CCEGLView.cpp
  2. ...
  3. CCEGLView* CCEGLView::sharedOpenGLView()
  4. {
  5. static CCEGLView* s_pEglView = NULL;
  6. if (s_pEglView == NULL)
  7. {
  8. s_pEglView = new CCEGLView();
  9. }
  10. return s_pEglView;
  11. }
  12. ...
  13. // openglview 结束方法
  14. void CCEGLView::end()
  15. {
  16. /* Exits from GLFW */
  17. glfwTerminate();
  18. delete this;
  19. exit(0);
  20. }
 
end() 方法很简单,只需要看到最后一句 exit(0); 就明白了。
 
cocos2d-x 程序的结束流程
 
程序运行时期,由 mainLoop 方法维持运行着游戏之内的各个逻辑,当在弹出最后一个场景,或者直接调用 CCDirector::end(); 方法后,触发游戏的清理工作,执行 purgeDirector 方法,从而结束了 CCEGLView(不同平台不同封装,PC使用OpenGl封装,移动终端封装的为 OpenGl ES) 的运行,调用其 end() 方法,从而直接执行 exit(0); 退出程序进程,从而结束了整个程序的运行。(Android 平台的 end() 方法内部通过Jni 方法 terminateProcessJNI(); 调用 Java 实现的功能,其功能一样,直接结束了当前运行的进程)
 
从程序的 main 方法开始,再创建 AppDelegate 等对象,运行过程中确实通过 exit(0); 来退出程序。所以我们看到了 AppDelegate 构造函数被调用,而其析构函数没有被调用的现象。
 
exit(0); 的执行,意味着我们的程序完全结束,当然我们的进程资源也会被操作系统释放。但是注意,这里的在构造函数创建[资源],并且在析构函数中释放[资源] 并非绝对意义上的程序进程资源,在程序退出的时候,程序所使用的资源当然会被系统回收,但是如果我在构造函数调用网络接口初始化,析构在调用一次通知,所影响到的类似这种的 非本地资源 逻辑上的处理,而留下隐患。而通过理解 cocos2d-x 的运行机制,可以减少这种可能存在的隐患。
 
cocos2d-x 的整体把握
在本文通过解决一个小疑问,而去分析 cocos2d-x 游戏的运行流程,当然其中很多细致末叶我们并没有深入下去。不去解决这个疑问也可以,知道没有调用析构函数,那我就不调用便是 (这也是简单的解决方法,也不用觉得这不可行 )。这里只是借着这个疑问,对 cocos2d-x 的流程稍作探寻而已。也没有贴一堆 cocos2d-x 源码去分析,其思路也有迹可循。
 
什么是 cocos2d-x ,它是 cocos2d 一个 C++ 的实现,除 C++ 之外,有 python ,Objective-C 等其它语言的实现,那该怎么去理解 cocos2d ,可以这么理解,cocos2d 是一个编写 2D 游戏的通用形框架,这种框架提供了一个通用模型,而这种模型或者说架构是 无关语言与平台 的,说 cocos2d-x 使用 C++ 编写,其跨平台能力很强,但它能跑在浏览器上么?cocos2d 还是有着 html5 的实现,当然平台决定着语言的选择,而 cocos2d 能够适应这么多不同的语言和平台,其良好的设计,清晰的结构功不可没。 而对不同语言,对相同功能有着不同的封装,正如在本文问题中,在不同平台(Linux 和 Android),对相同功能有着不同的封装异曲同工。那么封装到最后,我们对 cocos2d 的理解就只剩下了,我们要写游戏,那么需要导演,场景、层、精灵、动作等 ~~ 组织好这个中之间的关系即可 ~

Cocos2d-x 程序是如何开始运行与结束的的更多相关文章

  1. 【转】Cocos2d-x 程序是如何开始运行与结束的

    转自:http://blog.leafsoar.com/archives/2013/05-05.html 题记:对于技术,我们大可不必挖得那么深,但一定要具备可以挖得很深的能力 问题的由来 怎么样使用 ...

  2. C#程序以管理员权限运行

    原文:C#程序以管理员权限运行 C#程序以管理员权限运行 在Vista 和 Windows 7 及更新版本的操作系统,增加了 UAC(用户账户控制) 的安全机制,如果 UAC 被打开,用户即使以管理员 ...

  3. 小程序 web 端实时运行工具

    微信小程序 web 端实时运行工具 https://chemzqm.github.io/wept/

  4. 黄聪:使用srvany.exe将任何程序作为Windows服务运行

    srvany.exe是什么? srvany.exe是Microsoft Windows Resource Kits工具集的一个实用的小工具,用于将任何EXE程序作为Windows服务运行.也就是说sr ...

  5. [技巧.Dotnet]轻松实现“强制.net程序以管理员身份运行”。

    使用场景: 程序中不少操作都需要特殊权限,有时为了方便,直接让程序以管理员方式运行. (在商业软件中,其实应该尽量避免以管理员身份运行.在安装或配置时,提前授予将相应权限.) 做法: 以C#项目为例: ...

  6. 使用srvany.exe将任何程序作为Windows服务运行

    使用srvany.exe将任何程序作为Windows服务运行 2011 年 3 月 7 日 !本文可能 超过1年没有更新,今后内容也许不会被维护或者支持,部分内容可能具有时效性,涉及技术细节或者软件使 ...

  7. .NET程序的编译和运行

    程序的编译和运行,总得来说大体是:首先写好的程序是源代码,然后编译器编译为本地机器语言,最后在本地操作系统运行. 下图为传统代码编译运行过程: .NET的编译和运行过程与之类似,首先编写好的源代码,然 ...

  8. 【转】 C#程序以管理员权限运行

    C#程序以管理员权限运行在Vista 和 Windows 7 及更新版本的操作系统,增加了 UAC(用户账户控制) 的安全机制,如果 UAC 被打开,用户即使以管理员权限登录,其应用程序默认情况下也无 ...

  9. DOS环境下含包并引用第三方jar的java程序的编译及运行

    DOS环境下含包并引用第三方jar的java程序的编译及运行 1.程序目录机构 bin:class文件生成目录 lib:第三方jar包目录 src:源程序文件目录 2.程序代码: 3.程序编译 jav ...

随机推荐

  1. APIO2014 爆零总结

    真心爆零 不要不服 这次apio给了一种新的赛制 看上去很好? 所有人都可以在线提交 并且实时知道自己的分数 它对每个题目分成若干分数段 每个分数段有若干数据 要获得这个分数段的分数需要通过这个分数段 ...

  2. 轻松学习Linux之认识内存管理机制

    本文出自 "李晨光原创技术博客" 博客,谢绝转载!

  3. 2016 CCPC 杭州站 小结

    5题倒数第一,铜……(我就知道我们很稳!!!哼!! 这一次心态完全爆炸 开场我就没有按照平时的顺序读题 然后zr的A题wa 我F题T xl说B是一个最小生成树,又说是最小树形图,不会写 K题完全没思路 ...

  4. 1.VS2010C++环境设置

    一.需要下载的软件 1.visual studio 2010\\xxzx\tools\编程工具\MICROSOFT\VISUAL.STUDIO\VISUAL.STUDIO.201032位cn_visu ...

  5. ASP.NET的分页方法(一)

    要做一个关于分页写法的专题,这是今天的第一讲,自制分页,可能有些代码需要优化,希望大家给出一些中肯的建议 前台使用的repeater绑定的数据: <form id="form1&quo ...

  6. UVaLive 7503 Change (坑题。。。。。。)

    题意:给定两个人民币,问你花最少钱保证能够凑出另一个价格. 析:这个题最大的坑就是在,并一定是一次就凑出来,可以多次,然后就可以想了,如果要凑的数和1有关,特判,如果是2倍数,0.01就够了,否则就是 ...

  7. 更有效率的使用Visual Studio(一)

    很多比较通用的快捷键的默认设置其实是有一些缩写在里面的,这个估计也是MS帮助我们记忆.比如说注释代码的快捷键是Ctrl + E + C,我们如果知道它是 Ctrl + Edit + Comment C ...

  8. 集合类 Contains 方法 深入详解 与接口的实例

    .Net 相等性:集合类 Contains 方法 深入详解 http://www.cnblogs.com/ldp615/archive/2009/09/05/1560791.html 1.接口的概念及 ...

  9. VC++中几种字符标志的解释

    VC++中几种字符标志的解释 LPSTR = char * LPCSTR = const char * LPWSTR = wchar_t * LPCWSTR = const wchar_t * LPO ...

  10. T4模板语法

    T4,即4个T开头的英文字母组合:Text Template Transformation Toolkit. T4文本模板,即一种自定义规则的代码生成器.根据业务模型可生成任何形式的文本文件或供程序调 ...