当前位置: 首页 > news >正文

C++语言编程规范-资源分配和释放

01  

原则 6.1 明确产品动态内存的申请与释放原则

说明:之所以存在大量内存问题,主要原因是申请与释放内存的规则混乱:

02  

规则 6.1 明确 operator new 的行为和检查策略

说明:当 operator new 无法满足某一内存分配需求时,默认会抛异常,也可以返回空指针(通过编译选项设

置)。团队明确 operator new 的操作。在申请内存后,要立即检查指针是否为 NULL 或进行异常处理,C++

示例:捕获异常来处理申请内存失败情况

    char* pBuffer = NULL;  try  {  pBuffer = new char[BUFFER_SIZE];  }  catch (...)  {  pBuffer = NULL;  return EF_FAIL;  }// 或进行非空判断:char* pBuffer = new char[BUFFER_SIZE];  if (NULL == pBuffer)  {  return ER_FAIL;  }

    03  

    规则 6.2 释放内存后,要立即将指针设置为 NULL,防止产生野指针

    说明:free 或 delete 释放内存后,立即将指针设置为 NULL,防止产生“野指针”。这种判断最好能够封装

    起来,见建议 6.2。

    示例:

      char* pBuffer = new char[BUFFER_SIZE];  //…  delete [] pBuffer;  pBuffer = NULL;

      04  

      规则 6.3 单个对象释放使用 delete,数组对象释放使用 delete []

      说明:单个对象删除使用 delete, 数组对象删除使用 delete []。

      原因:调用 new 所包含的动作:从系统中申请一块内存,若是对象调用相应的构造函数。调用 new[n]

      所包含的动作:申请可容纳 n 个对象外加一点内存来保存数组的元素的数量;调用 n 次构造函数初始化这

      块内存中的 n 个对象。调用 delete 所包含的动作:若是对象调用相应的析构函数;将内存归还系统。

      调用 delete[]所包含的动作:从 new[]将找出的 n 值;调用 n 次相应的析构函数;将内存归还给系

      统。

      示例:

        std::string *string = new std::string;std::string *stringArray = new std::string[100];  delete string;  string = NULL;  delete [] stringArray;  stringArray = NULL;

        如果使用 delete stringArray;会导致 stringArray 指向的 100 个 string 对象中的 99 个未被销毁,因为它

        们的析构函数根本没有被调用,并且结果是不可预测的,编译器不同,结果也不同。

        05  

        规则 6.4 释放结构(类)指针时,首先释放其成员指针的内存空间

        示例:下面是一段有内存泄漏的产品代码:

          struct STORE_BUF_S  {    ULONG ulLen;    UCHAR *pcData;  }STORE_BUF_T;  void func()  {    STORE_BUF_T *pstStorageBuff = NULL;    //申请结构内存….    //程序处理…    free(pstStorageBuff);    return;  }

          先删除了 pstStorageBuff,pstStorageBuff->pcData 永远不可能被删除了。删除结构指针时,必须从底层向上层顺序删除。即:

            free (pstStorageBuff->pcData);  free(pstStorageBuff);

            06  

            规则 6.5 释放指针数组时,首先释放数组每个元素指针的内存

            说明:在释放指针数组时,确保数组中的每个元素指针是否已经被释放了,这样才不会导致内存泄漏。

            示例:

              struct dirent **namelist;  int n = scandir(path.c_str(), &namelist, 0, alphasort);//【1】int i = 0;  for(i ; i < n; ++i)  {  string name = namelist[i]->d_name;  free(namelist[i]); //【2】if(name != ".." && name != ".")  {  //.........  ++fileNum;  if(MAX_SCAN_FILE_NUM == fileNum )//MAX_SCAN_FILE_NUM=1000  {  break;  }  }  }  free(namelist); //【3】return ;

              由系统函数进行分配内存(如【1】所示),内存释放时分别由【2】释放数组单个元素和【3】释放数组本身内存一起完成的。 但是中间有个条件,每次只取 1000 个文件,如果目录下的文件大于 1000 就跳出,后面的就不会再处理 (【2】没有执行到)。当本地目录下文件数比较小,小于等于 1000 时没有内存泄漏;而当本地目录下的 文件大于 1000 时,就会导致内存泄漏。所以释放指针数组时,请注意首先释放其每个元素的内存空间。正确的做法是在【3】之前加上:

                for(int j = i ; j < n; ++j)  {    free(namelist[i]);  }

                07  

                规则 6.6 不要返回局部对象指针

                说明:局部对象在定义点构造,在同一作用域结束时立即被销毁。

                示例:

                  char* GetParameter ()  {  CDBConnect DBConnect;  //………………..  return DBConnect.GetString("ParamValue");  }

                  由于对象 DBConnect 已经析构,对应的指针已经被释放,从而后续访问非法内存,导致系统coredump。

                  08  

                  规则 6.7 不要强制关闭线程

                  说明:线程被强制关闭,导致线程内部资源泄漏。用事件或信号量通知线程,确保线程调用自身的退出函数。线程死锁需要强制关闭的情况除外。

                  示例:强制关闭线程,导致线程资源泄漏。

                    CShakeHand::CShakeHand()  {  m_hdShakeThreadrecv = CreateThread(NULL, 0,  (LPTHREAD_START_ROUTINE)ThreadProc_ShakeHands,  this, NULL, &m_ulShakeThreadID);  }  CShakeHand::~CShakeHand()  {  TerminateThread(m_hdShakeThreadrecv, 0); //强制关闭CloseHandle(m_hdShakeThreadrecv);  }

                    09  

                    建议 6.1 使用 new, delete 的封装方式来分配与释放内存

                    说明:推荐使用类似的如下宏,可以在一定程度上避免使用空指针,野指针的问题。

                      #define H7W_NEW(var, classname) \do { \try \{ \var = new classname; \} \catch (...) \{ \var = NULL; \} \break; \} while(0)//(1) 该宏会将var置为NULL, 所以调用该宏之后, 不再需要置var为NULL  //(2) HW_DELETE宏与NEW对应, 用来释放由HW_NEW分配的对象// 注意: 如果以数组方式分配对象(见对HW_NEW的描述), 则必须使用宏HW_DELETE_A
                        // 来释放, 否则可能导致问题,参见:规则6.3  #define HW_DELETE(var) \do \{ \if (var != NULL) \{ \delete var; \var = NULL; \} \break; \} while(NULL == var)  //(1) 这个宏用来删除一个由HW_NEW分配的数组, 删除之后也会将var置为NULL  #define HW_DELETE_A(var) \do \{ \if (var != NULL) \{ \delete []var; \var = NULL; \} \break; \} while(NULL == var)

                        直接使用 HW_DELETE,HW_DELETE_A 宏来释放指针内存空间,就不会出现遗忘将指针置为 NULL了。

                        10  

                        建议 6.3 使用 RAII 特性来帮助追踪动态分配

                        说明:RAII 是“资源获取就是初始化”的缩语(Resource Acquisition Is Initialization),是一种利用对象生命周期来控制程序资源(如内存、文件句柄、网络连接、互斥量等等)的简单技术。

                        RAII 的一般做法是这样的:在对象构造时获取资源,接着控制对资源的访问使之在对象的生命周期内 始终保持有效,最后在对象析构的时候释放资源。这种做法有两大好处:我们不需要显式地释放资源。

                        对象所需的资源在其生命期内始终保持有效。这样,就不必检查资源有效性的问题,可以简化逻辑、提高效率。

                        C++类库的智能指针就是其中之一:auto_ptr 是标准 C++库提供的一种模板类,使用指针进行初始化,其访问方式也和指针一样。在 auto_ptr 退出作用域时,所指对象能被隐式的自动删除。这样可以象使用普通

                        指针一样使用 auto_ptr,而不用考虑释放问题。注意:auto_ptr 的复制会造成它本身的修改,原有的auto_ptr 将不再指向任何对象,而由新的 auto_ptr 接管对象内存,并负责自动删除。因此 auto_ptr 复制后

                        不能再使用,且不能复制 const auto_ptr,因此不应使用 auto_ptr。

                        boost 库中提供了一种新型的智能

                        指针 shared_ptr,它解决了多个指针间共享对象所有权的问题,同时也满足容器对元素的要求,因而可以安全地放入容器中。

                        shared_ptr 解决了 auto_ptr 移动语义的破坏性。

                        关于 auto_ptr 与 shared_ptr 使用请参考 C++标准库的相关书籍。

                        示例:使用 RAII 不需要显式地释放互斥资源。

                          class My_scope_lock  {  public:  My_scope_lock(LockType& _lock):m_lock(_lock)  {  m_lock.occupy();  }  ~My_scope_lock()  {  m_lock.relase();  }  protected:  LockType m_lock;  }bool class Data::Update()  {  My_scope_lock l_lock(m_mutex_lock);  if()  {  return false;  }  else  {  //execute  }  return true;  }

                          http://www.dtcms.com/a/461786.html

                          相关文章:

                        • 影视广告网站重庆网站建设制作
                        • Hadess入门到实战(9) - 如何管理Composer(PHP)制品
                        • 如何设计公司官网站苏宁易购网站风格
                        • wx小程序扫码入口方式
                        • Agent 开发设计模式(Agentic Design Patterns )第 1 章:提示词链
                        • asp美食网站源码天津网站推广
                        • 图像处理踩坑:浮点数误差导致的缩放尺寸异常与解决办法
                        • Android Studio Meerkat 打开flutter项目没有自动选中main.dart configuration
                        • OpenTiny TinyEngine 基础知识
                        • 大模型-旋转位置编码(Rotary Positional Embedding)
                        • 如何减小ES和mysql的同步时间差
                        • this.$router.push 与 this.$router.replace 跳转的区别
                        • 网站域名到期时间查询网站建设蛋蛋28
                        • 建设网站选题应遵循的规则网站网页打开的速度什么决定的
                        • 【Servlet】使用idea2023创建Servlet JavaWeb
                        • 异步串口通信和逻辑分析仪
                        • 中微电力建设公司网站建设人行官方网站
                        • crew AI笔记[9] - 运用crew AI框架构建实战级agent项目
                        • 12.MySQL使用C连接和连接池
                        • Spring前置准备(八)——ConfigurableApplicationContext和DefaultListableBeanFactory的区别
                        • 鸿蒙开发5--鸿蒙页面导航(声明式导航Navigation组件)
                        • sql题目练习——单表查询
                        • html怎么做成网站多软件网站下载安装
                        • 做网站服务销售wordpress user level
                        • 【C++贪心】P10537 [APIO2024] 九月|普及+
                        • 大模型问答原理解析
                        • 【二叉树的递归算法与层序遍历算法】
                        • 【STM32】I2C通信—软件模拟
                        • 公司网站怎么修改内容wordpress 4.7.2 中文
                        • 怎么建手机网站seo新方法