new在C++中是一个我们经常用到的运算符。由它所创建的变量会被分配在堆中,并且在程序结束之前应当将分配的内存delete掉,否则就会导致内存泄漏。但是除此之外,你对new有更深入的了解吗?本篇文章将深入探讨C++中的new运算符,我在这篇文章中总结了new的以下知识点:

1. new内存分配失败后的处理

如果你认为new分配失败后应当如以下代码处理:

  1. int*p=new int;
  2. if(!p)
  3. {
  4. //error processing
  5. }

  那么你就大错特错了。C++中规定当new分配失败时,它会抛出一个bad_alloc exception,此时应当采用异常处理的方法:

  1. try
  2. {
  3. int* pStr=new string[SIZE];
  4. }
  5. catch(const bad_alloc& e)
  6. {
  7. //error processing
  8. }

  2. new的三种形态。

new实际上有三种形态:new operator、operator new和placement new。

new operator是我们最常用的形态(用法如上文的代码所示),它是语言内建的,不能重载,也不能改变其行为。它在执行过程中,与其余的两种形态都发生了密切的关系:第一步通过operator new来实现内存申请,第二步通过placement new来调用构造函数。

operator new在默认情况下为调用分配内存的代码,尝试从堆上得到一段空间,如果分配成功则直接返回,分配失败则调用new_handler函数,然后继续重复前面的过程,直到抛出了异常为止。如下的代码便重载了A的operator new函数:

  1. class A
  2. {
  3. public:
  4. A(int a);
  5. ~A();
  6. void* operator new(size_t size);
  7. }
  8.  
  9. void* A::operator new(size_t size)
  10. {
  11. cout<<"A's operator new"<<endl;
  12. return ::operator new(size);
  13. }

  这里调用了全局的new来进行内存分配。

placement new则是决定在分配的空间里采用哪种构造函数。通常情况下,构造函数是由编译器自动调用的,但你也可以手动调用构造函数。如以下代码所示:

  1. #include <iostream>
  2. class X
  3. {
  4. public:
  5. X() { std::cout << "constructor of X" << std::endl; }
  6. ~X() { std::cout << "destructor of X" << std::endl; }
  7.  
  8. void SetNum(int n)
  9. {
  10. num = n;
  11. }
  12.  
  13. int GetNum()
  14. {
  15. return num;
  16. }
  17.  
  18. private:
  19. int num;
  20. };
  21.  
  22. int main()
  23. {
  24. char* buf = new char[sizeof(X)];
  25. X *px = new(buf)X;
  26. px->SetNum(10);
  27. std::cout << px->GetNum() << std::endl;
  28. px->~X();
  29. delete[]buf;
  30.  
  31. return 0;
  32. }

  当然,如果显示地调用placement new,那么也得显示地调用对应的placement delete。

placement new的意义在于,operator new开辟内存是比较花费时间的,因此你可以一次用operator new开辟一片内存,然后利用placement new重复地在这片内存上构建对象,这样可以省去很多时间。

3. new_handler函数

在使用operator new申请内存失败之后, 编译器会调用new_handler函数来进行相应的处理。你可以定制属于自己的new_handler函数,只需要调用<new>中的set_new_handler之中即可,set_new_handler的参数为一个函数指针,返回值为指向的是set_new_handler调用之前的异常处理函数。

我们也可以根据类设定不同的new_handler函数,以下是一个例子:

  1. class A
  2. {
  3. public:
  4. static std::new_handler set_new_handler(std::new_handler p)throw();
  5. static void * operator new(std::size_t size) throw(std::bad_alloc);
  6. static void MemoryErrorHandling(); // new_handler function
  7. private:
  8. static std::new_handler m_curHandler;
  9. };
  10. std::new_handler A::m_curHandler=NULL;
  11. std::new_handler A::set_new_handler(std::new_handler p)throw()
  12. {
  13. std::new_handler old_handler=m_curHandler;
  14. m_curHandler=p;
  15. return old_handler;
  16. }
  17.  
  18. void MemoryErrorHandling()
  19. {
  20. //...
  21. }
  22.  
  23. void *operator new (std::size_t size)throw(std::bad_alloc)
  24. {
  25. set_new_handler(MemoryErrorHandling);
  26. return ::operator new(size);
  27. }

  

随机推荐

  1. 小程序——如何引入外部js

    当写小程序需要引入一些额外的js文件时,可以这样: 一.先把外部js用一个函数封闭起来: test.js function myfunc() { console.log("myfunc... ...

  2. jQuery-4.动画篇---jQuery核心

    jQuery中each方法的应用 jQuery中有个很重要的核心方法each,大部分jQuery方法在内部都会调用each,其主要的原因的就是jQuery的实例是一个元素合集 如下:找到所有的div, ...

  3. java Boolean和boolean的区别

    Boolean b1=new Boolean("false"); Boolean b2=new Boolean("tRue"); Boolean b3=new ...

  4. Unity中对系统类进行扩展的方法

    Unity扩展系统类,整合简化代码 本文提供全流程,中文翻译. Chinar 坚持将简单的生活方式,带给世人!(拥有更好的阅读体验 -- 高分辨率用户请根据需求调整网页缩放比例) Chinar -- ...

  5. vue打包后接口报错

    最近自己和朋友做了一个小的项目,用的是vue3.x版本,本地dev运行的时候接口什么的都是正常的,但是build打包后本地使用anywhere启动一个本地服务的时候发现接口报错405状态,发布到线上接 ...

  6. redis介绍、安装、redis持久化、redis数据类型

    1.redis介绍  2.安装管网:https://redis.io/下载:wget -c http://download.redis.io/releases/redis-4.0.11.tar.gz解 ...

  7. What is the Annotation?

    Annotation称为注释或注解,它是一个接口.注解提供了一种为程序元素(类.方法.成员变量等)设置元数据(描述其它数据的数据)的方法.编译器.开发工具或其它程序中可以通过反射来获取程序中的Anno ...

  8. vue中父组件给子组件额外添加参数

    1 子组件: this.$emit('callbackone',item.parentId) 2 父组件: @callbackone="callbackone($event,index)&q ...

  9. magento 1.9 nginx 404

    原来的nginx 配置 lnmp 环境默认的 location ~ [^/]\.php(/|$) { fastcgi_param SCRIPT_FILENAME $document_root$fast ...

  10. Java高级特性 第7节 多线程

    一.进程与线程的概念 1. 进程 进程是应用程序的执行实例,有独立的内存空间和系统资源. 如上图,标红色的是一个Office Word进程. 进程的特点: 动态性:进程是动态的创建和消亡: 并发性:操 ...